From 0f3f8f68f87233bf59c3fa68c3dfe1f492a1a478 Mon Sep 17 00:00:00 2001 From: inmarket Date: Wed, 20 Aug 2014 17:42:53 +1000 Subject: Rename lots of files to help prevent compile time name conflicts. --- src/gadc/gadc.c | 362 ---- src/gadc/gadc_gadc.c | 362 ++++ src/gadc/sys_make.mk | 2 +- src/gaudio/gaudio.c | 283 --- src/gaudio/gaudio_gaudio.c | 283 +++ src/gaudio/sys_make.mk | 2 +- src/gdisp/colors.h | 377 ---- src/gdisp/fonts.c | 96 - src/gdisp/fonts/build_fonts.sh | 2 +- src/gdisp/fonts/fonts.h | 34 +- src/gdisp/gdisp.c | 3100 ------------------------------ src/gdisp/gdisp_colors.h | 377 ++++ src/gdisp/gdisp_fonts.c | 96 + src/gdisp/gdisp_gdisp.c | 3100 ++++++++++++++++++++++++++++++ src/gdisp/gdisp_image.c | 233 +++ src/gdisp/gdisp_image.h | 315 +++ src/gdisp/gdisp_image_bmp.c | 904 +++++++++ src/gdisp/gdisp_image_gif.c | 1208 ++++++++++++ src/gdisp/gdisp_image_jpg.c | 19 + src/gdisp/gdisp_image_native.c | 145 ++ src/gdisp/gdisp_image_png.c | 19 + src/gdisp/image.c | 233 --- src/gdisp/image.h | 315 --- src/gdisp/image_bmp.c | 904 --------- src/gdisp/image_gif.c | 1208 ------------ src/gdisp/image_jpg.c | 19 - src/gdisp/image_native.c | 145 -- src/gdisp/image_png.c | 19 - src/gdisp/sys_defs.h | 4 +- src/gdisp/sys_make.mk | 16 +- src/gevent/gevent.c | 246 --- src/gevent/gevent_gevent.c | 246 +++ src/gevent/sys_make.mk | 2 +- src/gfile/fatfs_chibios_diskio.c | 254 --- src/gfile/fatfs_wrapper.c | 89 - src/gfile/fatfs_wrapper.h | 32 - src/gfile/gfile.c | 407 ---- src/gfile/gfile_fatfs_diskio_chibios.c | 260 +++ src/gfile/gfile_fatfs_wrapper.c | 89 + src/gfile/gfile_fatfs_wrapper.h | 32 + src/gfile/gfile_fs_chibios.c | 68 + src/gfile/gfile_fs_fatfs.c | 325 ++++ src/gfile/gfile_fs_mem.c | 65 + src/gfile/gfile_fs_native.c | 234 +++ src/gfile/gfile_fs_petitfs.c | 157 ++ src/gfile/gfile_fs_ram.c | 32 + src/gfile/gfile_fs_rom.c | 178 ++ src/gfile/gfile_fs_strings.c | 138 ++ src/gfile/gfile_gfile.c | 407 ++++ src/gfile/gfile_petitfs_diskio_chibios.c | 88 + src/gfile/gfile_petitfs_wrapper.c | 23 + src/gfile/gfile_petitfs_wrapper.h | 26 + src/gfile/gfile_printg.c | 223 +++ src/gfile/gfile_scang.c | 234 +++ src/gfile/gfile_stdio.c | 47 + src/gfile/inc_chibiosfs.c | 68 - src/gfile/inc_fatfs.c | 325 ---- src/gfile/inc_memfs.c | 65 - src/gfile/inc_nativefs.c | 235 --- src/gfile/inc_petitfs.c | 157 -- src/gfile/inc_printg.c | 223 --- src/gfile/inc_ramfs.c | 32 - src/gfile/inc_romfs.c | 178 -- src/gfile/inc_scang.c | 234 --- src/gfile/inc_stdio.c | 47 - src/gfile/inc_strings.c | 138 -- src/gfile/petitfs_chibios_diskio.c | 82 - src/gfile/petitfs_wrapper.c | 23 - src/gfile/petitfs_wrapper.h | 26 - src/gfile/sys_make.mk | 32 +- src/ginput/dial.c | 153 -- src/ginput/dial.h | 111 -- src/ginput/ginput.c | 36 - src/ginput/ginput_dial.c | 153 ++ src/ginput/ginput_dial.h | 111 ++ src/ginput/ginput_ginput.c | 36 + src/ginput/ginput_keyboard.c | 23 + src/ginput/ginput_keyboard.h | 128 ++ src/ginput/ginput_mouse.c | 679 +++++++ src/ginput/ginput_mouse.h | 181 ++ src/ginput/ginput_toggle.c | 156 ++ src/ginput/ginput_toggle.h | 100 + src/ginput/keyboard.c | 23 - src/ginput/keyboard.h | 128 -- src/ginput/mouse.c | 679 ------- src/ginput/mouse.h | 181 -- src/ginput/sys_defs.h | 8 +- src/ginput/sys_make.mk | 10 +- src/ginput/toggle.c | 156 -- src/ginput/toggle.h | 100 - src/gmisc/arrayops.c | 226 --- src/gmisc/gmisc.c | 27 - src/gmisc/gmisc_arrayops.c | 226 +++ src/gmisc/gmisc_gmisc.c | 27 + src/gmisc/gmisc_trig.c | 190 ++ src/gmisc/sys_make.mk | 6 +- src/gmisc/trig.c | 190 -- src/gqueue/gqueue.c | 469 ----- src/gqueue/gqueue_gqueue.c | 469 +++++ src/gqueue/sys_make.mk | 2 +- src/gtimer/gtimer.c | 238 --- src/gtimer/gtimer_gtimer.c | 238 +++ src/gtimer/sys_make.mk | 2 +- src/gwin/button.c | 328 ---- src/gwin/button.h | 135 -- src/gwin/checkbox.c | 195 -- src/gwin/checkbox.h | 127 -- src/gwin/class_gwin.h | 363 ---- src/gwin/console.c | 809 -------- src/gwin/console.h | 178 -- src/gwin/frame.c | 322 ---- src/gwin/frame.h | 95 - src/gwin/gcontainer.c | 201 -- src/gwin/gcontainer.h | 161 -- src/gwin/gimage.c | 162 -- src/gwin/gimage.h | 127 -- src/gwin/graph.c | 338 ---- src/gwin/graph.h | 186 -- src/gwin/gwidget.c | 516 ----- src/gwin/gwidget.h | 391 ---- src/gwin/gwin.c | 375 ---- src/gwin/gwin_button.c | 328 ++++ src/gwin/gwin_button.h | 135 ++ src/gwin/gwin_checkbox.c | 195 ++ src/gwin/gwin_checkbox.h | 127 ++ src/gwin/gwin_class.h | 363 ++++ src/gwin/gwin_console.c | 809 ++++++++ src/gwin/gwin_console.h | 178 ++ src/gwin/gwin_container.c | 201 ++ src/gwin/gwin_container.h | 161 ++ src/gwin/gwin_frame.c | 322 ++++ src/gwin/gwin_frame.h | 97 + src/gwin/gwin_gl3d.c | 2 +- src/gwin/gwin_gl3d.h | 2 +- src/gwin/gwin_graph.c | 338 ++++ src/gwin/gwin_graph.h | 186 ++ src/gwin/gwin_gwin.c | 380 ++++ src/gwin/gwin_image.c | 162 ++ src/gwin/gwin_image.h | 127 ++ src/gwin/gwin_label.c | 168 ++ src/gwin/gwin_label.h | 104 + src/gwin/gwin_list.c | 696 +++++++ src/gwin/gwin_list.h | 304 +++ src/gwin/gwin_progressbar.c | 316 +++ src/gwin/gwin_progressbar.h | 210 ++ src/gwin/gwin_radio.c | 280 +++ src/gwin/gwin_radio.h | 144 ++ src/gwin/gwin_slider.c | 376 ++++ src/gwin/gwin_slider.h | 157 ++ src/gwin/gwin_widget.c | 521 +++++ src/gwin/gwin_widget.h | 391 ++++ src/gwin/gwin_wm.c | 852 ++++++++ src/gwin/gwm.c | 847 -------- src/gwin/label.c | 168 -- src/gwin/label.h | 104 - src/gwin/list.c | 696 ------- src/gwin/list.h | 304 --- src/gwin/progressbar.c | 316 --- src/gwin/progressbar.h | 209 -- src/gwin/radio.c | 280 --- src/gwin/radio.h | 144 -- src/gwin/slider.c | 376 ---- src/gwin/slider.h | 157 -- src/gwin/sys_defs.h | 12 +- src/gwin/sys_make.mk | 30 +- 165 files changed, 21362 insertions(+), 21333 deletions(-) delete mode 100644 src/gadc/gadc.c create mode 100644 src/gadc/gadc_gadc.c delete mode 100644 src/gaudio/gaudio.c create mode 100644 src/gaudio/gaudio_gaudio.c delete mode 100644 src/gdisp/colors.h delete mode 100644 src/gdisp/fonts.c delete mode 100644 src/gdisp/gdisp.c create mode 100644 src/gdisp/gdisp_colors.h create mode 100644 src/gdisp/gdisp_fonts.c create mode 100644 src/gdisp/gdisp_gdisp.c create mode 100644 src/gdisp/gdisp_image.c create mode 100644 src/gdisp/gdisp_image.h create mode 100644 src/gdisp/gdisp_image_bmp.c create mode 100644 src/gdisp/gdisp_image_gif.c create mode 100644 src/gdisp/gdisp_image_jpg.c create mode 100644 src/gdisp/gdisp_image_native.c create mode 100644 src/gdisp/gdisp_image_png.c delete mode 100644 src/gdisp/image.c delete mode 100644 src/gdisp/image.h delete mode 100644 src/gdisp/image_bmp.c delete mode 100644 src/gdisp/image_gif.c delete mode 100644 src/gdisp/image_jpg.c delete mode 100644 src/gdisp/image_native.c delete mode 100644 src/gdisp/image_png.c delete mode 100644 src/gevent/gevent.c create mode 100644 src/gevent/gevent_gevent.c delete mode 100644 src/gfile/fatfs_chibios_diskio.c delete mode 100644 src/gfile/fatfs_wrapper.c delete mode 100644 src/gfile/fatfs_wrapper.h delete mode 100644 src/gfile/gfile.c create mode 100644 src/gfile/gfile_fatfs_diskio_chibios.c create mode 100644 src/gfile/gfile_fatfs_wrapper.c create mode 100644 src/gfile/gfile_fatfs_wrapper.h create mode 100644 src/gfile/gfile_fs_chibios.c create mode 100644 src/gfile/gfile_fs_fatfs.c create mode 100644 src/gfile/gfile_fs_mem.c create mode 100644 src/gfile/gfile_fs_native.c create mode 100644 src/gfile/gfile_fs_petitfs.c create mode 100644 src/gfile/gfile_fs_ram.c create mode 100644 src/gfile/gfile_fs_rom.c create mode 100644 src/gfile/gfile_fs_strings.c create mode 100644 src/gfile/gfile_gfile.c create mode 100644 src/gfile/gfile_petitfs_diskio_chibios.c create mode 100644 src/gfile/gfile_petitfs_wrapper.c create mode 100644 src/gfile/gfile_petitfs_wrapper.h create mode 100644 src/gfile/gfile_printg.c create mode 100644 src/gfile/gfile_scang.c create mode 100644 src/gfile/gfile_stdio.c delete mode 100644 src/gfile/inc_chibiosfs.c delete mode 100644 src/gfile/inc_fatfs.c delete mode 100644 src/gfile/inc_memfs.c delete mode 100644 src/gfile/inc_nativefs.c delete mode 100644 src/gfile/inc_petitfs.c delete mode 100644 src/gfile/inc_printg.c delete mode 100644 src/gfile/inc_ramfs.c delete mode 100644 src/gfile/inc_romfs.c delete mode 100644 src/gfile/inc_scang.c delete mode 100644 src/gfile/inc_stdio.c delete mode 100644 src/gfile/inc_strings.c delete mode 100644 src/gfile/petitfs_chibios_diskio.c delete mode 100644 src/gfile/petitfs_wrapper.c delete mode 100644 src/gfile/petitfs_wrapper.h delete mode 100644 src/ginput/dial.c delete mode 100644 src/ginput/dial.h delete mode 100644 src/ginput/ginput.c create mode 100644 src/ginput/ginput_dial.c create mode 100644 src/ginput/ginput_dial.h create mode 100644 src/ginput/ginput_ginput.c create mode 100644 src/ginput/ginput_keyboard.c create mode 100644 src/ginput/ginput_keyboard.h create mode 100644 src/ginput/ginput_mouse.c create mode 100644 src/ginput/ginput_mouse.h create mode 100644 src/ginput/ginput_toggle.c create mode 100644 src/ginput/ginput_toggle.h delete mode 100644 src/ginput/keyboard.c delete mode 100644 src/ginput/keyboard.h delete mode 100644 src/ginput/mouse.c delete mode 100644 src/ginput/mouse.h delete mode 100644 src/ginput/toggle.c delete mode 100644 src/ginput/toggle.h delete mode 100644 src/gmisc/arrayops.c delete mode 100644 src/gmisc/gmisc.c create mode 100644 src/gmisc/gmisc_arrayops.c create mode 100644 src/gmisc/gmisc_gmisc.c create mode 100644 src/gmisc/gmisc_trig.c delete mode 100644 src/gmisc/trig.c delete mode 100644 src/gqueue/gqueue.c create mode 100644 src/gqueue/gqueue_gqueue.c delete mode 100644 src/gtimer/gtimer.c create mode 100644 src/gtimer/gtimer_gtimer.c delete mode 100644 src/gwin/button.c delete mode 100644 src/gwin/button.h delete mode 100644 src/gwin/checkbox.c delete mode 100644 src/gwin/checkbox.h delete mode 100644 src/gwin/class_gwin.h delete mode 100644 src/gwin/console.c delete mode 100644 src/gwin/console.h delete mode 100644 src/gwin/frame.c delete mode 100644 src/gwin/frame.h delete mode 100644 src/gwin/gcontainer.c delete mode 100644 src/gwin/gcontainer.h delete mode 100644 src/gwin/gimage.c delete mode 100644 src/gwin/gimage.h delete mode 100644 src/gwin/graph.c delete mode 100644 src/gwin/graph.h delete mode 100644 src/gwin/gwidget.c delete mode 100644 src/gwin/gwidget.h delete mode 100644 src/gwin/gwin.c create mode 100644 src/gwin/gwin_button.c create mode 100644 src/gwin/gwin_button.h create mode 100644 src/gwin/gwin_checkbox.c create mode 100644 src/gwin/gwin_checkbox.h create mode 100644 src/gwin/gwin_class.h create mode 100644 src/gwin/gwin_console.c create mode 100644 src/gwin/gwin_console.h create mode 100644 src/gwin/gwin_container.c create mode 100644 src/gwin/gwin_container.h create mode 100644 src/gwin/gwin_frame.c create mode 100644 src/gwin/gwin_frame.h create mode 100644 src/gwin/gwin_graph.c create mode 100644 src/gwin/gwin_graph.h create mode 100644 src/gwin/gwin_gwin.c create mode 100644 src/gwin/gwin_image.c create mode 100644 src/gwin/gwin_image.h create mode 100644 src/gwin/gwin_label.c create mode 100644 src/gwin/gwin_label.h create mode 100644 src/gwin/gwin_list.c create mode 100644 src/gwin/gwin_list.h create mode 100644 src/gwin/gwin_progressbar.c create mode 100644 src/gwin/gwin_progressbar.h create mode 100644 src/gwin/gwin_radio.c create mode 100644 src/gwin/gwin_radio.h create mode 100644 src/gwin/gwin_slider.c create mode 100644 src/gwin/gwin_slider.h create mode 100644 src/gwin/gwin_widget.c create mode 100644 src/gwin/gwin_widget.h create mode 100644 src/gwin/gwin_wm.c delete mode 100644 src/gwin/gwm.c delete mode 100644 src/gwin/label.c delete mode 100644 src/gwin/label.h delete mode 100644 src/gwin/list.c delete mode 100644 src/gwin/list.h delete mode 100644 src/gwin/progressbar.c delete mode 100644 src/gwin/progressbar.h delete mode 100644 src/gwin/radio.c delete mode 100644 src/gwin/radio.h delete mode 100644 src/gwin/slider.c delete mode 100644 src/gwin/slider.h (limited to 'src') diff --git a/src/gadc/gadc.c b/src/gadc/gadc.c deleted file mode 100644 index d307519b..00000000 --- a/src/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.c - * @brief GADC sub-system code. - * - * @addtogroup GADC - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GADC - -/* Include the driver defines */ -#include "src/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_gadc.c b/src/gadc/gadc_gadc.c new file mode 100644 index 00000000..5ac8e53c --- /dev/null +++ b/src/gadc/gadc_gadc.c @@ -0,0 +1,362 @@ +/* + * 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/sys_make.mk b/src/gadc/sys_make.mk index 05b1e9cc..21a07dbe 100644 --- a/src/gadc/sys_make.mk +++ b/src/gadc/sys_make.mk @@ -1 +1 @@ -GFXSRC += $(GFXLIB)/src/gadc/gadc.c +GFXSRC += $(GFXLIB)/src/gadc/gadc_gadc.c diff --git a/src/gaudio/gaudio.c b/src/gaudio/gaudio.c deleted file mode 100644 index ab1f95ee..00000000 --- a/src/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.c - * @brief GAUDIO sub-system code. - * - * @addtogroup GAUDIO - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GAUDIO - -#if GAUDIO_NEED_PLAY - #include "src/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 "src/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_gaudio.c b/src/gaudio/gaudio_gaudio.c new file mode 100644 index 00000000..bb12e6d2 --- /dev/null +++ b/src/gaudio/gaudio_gaudio.c @@ -0,0 +1,283 @@ +/* + * 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/sys_make.mk b/src/gaudio/sys_make.mk index 438892c0..ea02e010 100644 --- a/src/gaudio/sys_make.mk +++ b/src/gaudio/sys_make.mk @@ -1 +1 @@ -GFXSRC += $(GFXLIB)/src/gaudio/gaudio.c +GFXSRC += $(GFXLIB)/src/gaudio/gaudio_gaudio.c diff --git a/src/gdisp/colors.h b/src/gdisp/colors.h deleted file mode 100644 index 6e9a7663..00000000 --- a/src/gdisp/colors.h +++ /dev/null @@ -1,377 +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/colors.h - * @brief GDISP color definitions header file. - * - * @defgroup Colors Colors - * @ingroup GDISP - * @{ - */ - -#ifndef _GDISP_COLORS_H -#define _GDISP_COLORS_H - -#include "gfx.h" - -#if GFX_USE_GDISP || defined(__DOXYGEN__) - -/** - * For pixel formats we do some assignment of codes to enable - * format auto-calculation. (Undocumented). - * 0x2RGB TRUECOLOR RGB format, R = red bits, G = green bits, B = blue bits - * 0x3RGB TRUECOLOR BGR format, R = red bits, G = green bits, B = blue bits - * 0x40XX GRAYSCALE XX = bits - * 0x60XX PALLETTE XX = bits - * 0x8XXX CUSTOM format. - */ -#define GDISP_COLORSYSTEM_MASK 0xF000 -#define GDISP_COLORSYSTEM_RGB 0x2000 -#define GDISP_COLORSYSTEM_BGR 0x3000 - -/** - * @brief Color Type Constants - * @{ - */ -#define GDISP_COLORSYSTEM_TRUECOLOR 0x2000 -#define GDISP_COLORSYSTEM_GRAYSCALE 0x4000 -#define GDISP_COLORSYSTEM_PALETTE 0x6000 -/** @} */ - -/** - * @brief Pixel Format Constants - * @{ - */ -#define GDISP_PIXELFORMAT_MONO (GDISP_COLORSYSTEM_GRAYSCALE|0x0001) -#define GDISP_PIXELFORMAT_GRAY4 (GDISP_COLORSYSTEM_GRAYSCALE|0x0002) -#define GDISP_PIXELFORMAT_GRAY16 (GDISP_COLORSYSTEM_GRAYSCALE|0x0004) -#define GDISP_PIXELFORMAT_GRAY256 (GDISP_COLORSYSTEM_GRAYSCALE|0x0008) -#define GDISP_PIXELFORMAT_RGB565 (GDISP_COLORSYSTEM_RGB|0x0565) -#define GDISP_PIXELFORMAT_BGR565 (GDISP_COLORSYSTEM_BGR|0x0565) -#define GDISP_PIXELFORMAT_RGB888 (GDISP_COLORSYSTEM_RGB|0x0888) -#define GDISP_PIXELFORMAT_BGR888 (GDISP_COLORSYSTEM_BGR|0x0888) -#define GDISP_PIXELFORMAT_RGB444 (GDISP_COLORSYSTEM_RGB|0x0444) -#define GDISP_PIXELFORMAT_BGR444 (GDISP_COLORSYSTEM_BGR|0x0444) -#define GDISP_PIXELFORMAT_RGB332 (GDISP_COLORSYSTEM_RGB|0x0332) -#define GDISP_PIXELFORMAT_BGR332 (GDISP_COLORSYSTEM_BGR|0x0332) -#define GDISP_PIXELFORMAT_RGB666 (GDISP_COLORSYSTEM_RGB|0x0666) -#define GDISP_PIXELFORMAT_BGR666 (GDISP_COLORSYSTEM_BGR|0x0666) -#define GDISP_PIXELFORMAT_ERROR 0x0000 -/** @} */ - -/** - * @name Some basic colors - * @{ - */ -#define White HTML2COLOR(0xFFFFFF) -#define Black HTML2COLOR(0x000000) -#define Gray HTML2COLOR(0x808080) -#define Grey Gray -#define Blue HTML2COLOR(0x0000FF) -#define Red HTML2COLOR(0xFF0000) -#define Fuchsia HTML2COLOR(0xFF00FF) -#define Magenta Fuchsia -#define Green HTML2COLOR(0x008000) -#define Yellow HTML2COLOR(0xFFFF00) -#define Aqua HTML2COLOR(0x00FFFF) -#define Cyan Aqua -#define Lime HTML2COLOR(0x00FF00) -#define Maroon HTML2COLOR(0x800000) -#define Navy HTML2COLOR(0x000080) -#define Olive HTML2COLOR(0x808000) -#define Purple HTML2COLOR(0x800080) -#define Silver HTML2COLOR(0xC0C0C0) -#define Teal HTML2COLOR(0x008080) -#define Orange HTML2COLOR(0xFFA500) -#define Pink HTML2COLOR(0xFFC0CB) -#define SkyBlue HTML2COLOR(0x87CEEB) -/** @} */ - -#if defined(__DOXYGEN__) - /** - * @brief The color system (grayscale, palette or truecolor) - */ - #define COLOR_SYSTEM GDISP_COLORSYSTEM_TRUECOLOR - /** - * @brief The number of bits in a color value - */ - #define COLOR_BITS 16 - /** - * @brief The number of bits for each of red, green and blue - * @{ - */ - #define COLOR_BITS_R 5 - #define COLOR_BITS_G 6 - #define COLOR_BITS_B 5 - /** @} */ - /** - * @brief The number of bits to shift each of red, green and blue to put it in the correct place in the color - * @{ - */ - #define COLOR_SHIFT_R 11 - #define COLOR_SHIFT_G 5 - #define COLOR_SHIFT_B 0 - /** @} */ - /** - * @brief Does the color need masking to remove invalid bits - */ - #define COLOR_NEEDS_MASK FALSE - /** - * @brief If the color needs masking to remove invalid bits, this is the mask - */ - #define COLOR_MASK 0xFFFF - /** - * @brief The color type - * @{ - */ - #define COLOR_TYPE uint16_t - /** @} */ - /** - * @brief The number of bits in the color type (not necessarily the same as COLOR_BITS). - */ - #define COLOR_TYPE_BITS 16 - /** - * @brief Convert a luminance (0 to 255) into a color value. - * @note The word "Luma" is used instead of grey or gray due to the spelling ambiguities of the word grey - * @note This is not a weighted luminance conversion in the color tv style. - * @note @p LUMA2COLOR() uses a linear conversion (0.33R + 0.33G + 0.33B). Note this is different to color - * tv luminance (0.26126R + 0.7152G + 0.0722B), digital tv luminance of (0.299R + 0.587G + 0.114B), or - * @p LUMA_OF() which uses (0.25R + 0.5G + 0.25B). - */ - #define LUMA2COLOR(l) ((color_t)((((l) & 0xF8)<<8) | (((l) & 0xFC)<<3) | (((l) & 0xF8)>>3))) - /** - * @brief Convert red, green, blue (each 0 to 255) into a color value. - */ - #define RGB2COLOR(r,g,b) ((color_t)((((r) & 0xF8)<<8) | (((g) & 0xFC)<<3) | (((b) & 0xF8)>>3))) - /** - * @brief Convert a 6 digit HTML code (hex) into a color value. - */ - #define HTML2COLOR(h) ((color_t)((((h) & 0xF80000)>>8) | (((h) & 0x00FC00)>>5) | (((h) & 0x0000F8)>>3))) - /** - * @brief Extract the luma/red/green/blue component (0 to 255) of a color value. - * @note This uses quick and dirty bit shifting. If you want more exact colors - * use @p EXACT_RED_OF() etc which uses multiplies and divides. For constant - * colors using @p EXACT_RED_OF() is no more expensive because the compiler - * evaluates the arithmetic. - * @note @p LUMA_OF() returns a roughly weighted luminance (0.25R + 0.5G + 0.25B). Note this is - * different to @p LUMA2COLOR() which uses a linear conversion (0.33R + 0.33G + 0.33B) and - * color tv luminance of (0.26126R + 0.7152G + 0.0722B) and digital tv luminance of (0.299R + 0.587G + 0.114B). - * @note A 5 bit color component maximum value (0x1F) converts to 0xF8 (slightly off-color) - * @{ - */ - #define LUMA_OF(c) ((RED_OF(c)+((uint16_t)GREEN_OF(c)<<1)+BLUE_OF(c))>>2) - #define RED_OF(c) (((c) & 0xF800)>>8) - #define GREEN_OF(c) (((c)&0x007E)>>3) - #define BLUE_OF(c) (((c)&0x001F)<<3) - /** @} */ - /** - * @brief Extract the exact luma/red/green/blue component (0 to 255) of a color value. - * @note This uses multiplies and divides rather than bit shifting. - * This gives exact equivalent colors at the expense of more cpu intensive - * operations. Note for constants this is no more expensive than @p REF_OF() - * because the compiler evaluates the arithmetic. - * @note @p EXACT_LUMA_OF() returns a roughly weighted luminance (0.25R + 0.5G + 0.25B). Note this is - * different to @p LUMA2COLOR() which uses a linear conversion (0.33R + 0.33G + 0.33B) and - * color tv luminance of (0.26126R + 0.7152G + 0.0722B) and digital tv luminance of (0.299R + 0.587G + 0.114B). - * @note A 5 bit color component maximum value (0x1F) converts to 0xFF (the true equivalent color) - * @{ - */ - #define EXACT_LUMA_OF(c) ((EXACT_RED_OF(c)+((uint16_t)EXACT_GREEN_OF(c)<<1)+EXACT_BLUE_OF(c))>>2) - #define EXACT_RED_OF(c) (((((c)>>11)&0x1F)*255)/31) - #define EXACT_GREEN_OF(c) (((((c)>>5)&0x3F)*255)/63) - #define EXACT_BLUE_OF(c) (((((c)>>0)&0x1F)*255)/31) - /** @} */ -#endif - -/* - * We use this big mess of macros to calculate all the components - * to prevent user errors in the color definitions. It greatly simplifies - * the above definitions and ensures a consistent implementation. - */ - -//------------------------- -// True-Color color system -//------------------------- -#if GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_TRUECOLOR - #define COLOR_SYSTEM GDISP_COLORSYSTEM_TRUECOLOR - - // Calculate the number of bits - #define COLOR_BITS_R ((GDISP_PIXELFORMAT>>8) & 0x0F) - #define COLOR_BITS_G ((GDISP_PIXELFORMAT>>4) & 0x0F) - #define COLOR_BITS_B ((GDISP_PIXELFORMAT>>0) & 0x0F) - #define COLOR_BITS (COLOR_BITS_R + COLOR_BITS_G + COLOR_BITS_B) - - // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking - #if COLOR_BITS <= 8 - #define COLOR_TYPE uint8_t - #define COLOR_TYPE_BITS 8 - #elif COLOR_BITS <= 16 - #define COLOR_TYPE uint16_t - #define COLOR_TYPE_BITS 16 - #elif COLOR_BITS <= 32 - #define COLOR_TYPE uint32_t - #define COLOR_TYPE_BITS 32 - #else - #error "GDISP: Cannot define color types with more than 32 bits" - #endif - #if COLOR_TYPE_BITS == COLOR_BITS - #define COLOR_NEEDS_MASK FALSE - #else - #define COLOR_NEEDS_MASK TRUE - #endif - #define COLOR_MASK() ((1 << COLOR_BITS)-1) - - // Calculate the component bit shifts - #if (GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_RGB - #define COLOR_SHIFT_R (COLOR_BITS_B+COLOR_BITS_G) - #define COLOR_SHIFT_G COLOR_BITS_B - #define COLOR_SHIFT_B 0 - #else - #define COLOR_SHIFT_B (COLOR_BITS_R+COLOR_BITS_G) - #define COLOR_SHIFT_G COLOR_BITS_R - #define COLOR_SHIFT_R 0 - #endif - - // Calculate RED_OF, GREEN_OF, BLUE_OF and RGB2COLOR - #if COLOR_BITS_R + COLOR_SHIFT_R == 8 - #define RED_OF(c) ((c) & (((1< 8 - #define RED_OF(c) (((c) & (((1<> (COLOR_BITS_R+COLOR_SHIFT_R-8)) - #define RGB2COLOR_R(r) (((COLOR_TYPE)((r) & (0xFF & ~((1<<(8-COLOR_BITS_R))-1)))) << (COLOR_BITS_R+COLOR_SHIFT_R-8)) - #else // COLOR_BITS_R + COLOR_SHIFT_R < 8 - #define RED_OF(c) (((c) & (((1<> (8-(COLOR_BITS_R+COLOR_SHIFT_R))) - #endif - #if COLOR_BITS_G + COLOR_SHIFT_G == 8 - #define GREEN_OF(c) ((c) & (((1< 8 - #define GREEN_OF(c) (((c) & (((1<> (COLOR_BITS_G+COLOR_SHIFT_G-8)) - #define RGB2COLOR_G(g) (((COLOR_TYPE)((g) & (0xFF & ~((1<<(8-COLOR_BITS_G))-1)))) << (COLOR_BITS_G+COLOR_SHIFT_G-8)) - #else // COLOR_BITS_G + COLOR_SHIFT_G < 8 - #define GREEN_OF(c) (((c) & (((1<> (8-(COLOR_BITS_G+COLOR_SHIFT_G))) - #endif - #if COLOR_BITS_B + COLOR_SHIFT_B == 8 - #define BLUE_OF(c) ((c) & (((1< 8 - #define BLUE_OF(c) (((c) & (((1<> (COLOR_BITS_B+COLOR_SHIFT_B-8)) - #define RGB2COLOR_B(b) (((COLOR_TYPE)((b) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1)))) << (COLOR_BITS_B+COLOR_SHIFT_B-8)) - #else // COLOR_BITS_B + COLOR_SHIFT_B < 8 - #define BLUE_OF(c) (((c) & (((1<> (8-(COLOR_BITS_B+COLOR_SHIFT_B))) - #endif - #define LUMA_OF(c) ((RED_OF(c)+((uint16_t)GREEN_OF(c)<<1)+BLUE_OF(c))>>2) - #define EXACT_RED_OF(c) (((uint16_t)(((c)>>COLOR_SHIFT_R)&((1<>COLOR_SHIFT_G)&((1<>COLOR_SHIFT_B)&((1<>2) - #define LUMA2COLOR(l) (RGB2COLOR_R(l) | RGB2COLOR_G(l) | RGB2COLOR_B(l)) - #define RGB2COLOR(r,g,b) (RGB2COLOR_R(r) | RGB2COLOR_G(g) | RGB2COLOR_B(b)) - - // Calculate HTML2COLOR - #if COLOR_BITS_R + COLOR_SHIFT_R == 24 - #define HTML2COLOR_R(h) ((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) - #elif COLOR_BITS_R + COLOR_SHIFT_R > 24 - #define HTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) << (COLOR_BITS_R+COLOR_SHIFT_R-24)) - #else // COLOR_BITS_R + COLOR_SHIFT_R < 24 - #define HTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) >> (24-(COLOR_BITS_R+COLOR_SHIFT_R))) - #endif - #if COLOR_BITS_G + COLOR_SHIFT_G == 16 - #define HTML2COLOR_G(h) ((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) - #elif COLOR_BITS_G + COLOR_SHIFT_G > 16 - #define HTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) << (COLOR_BITS_G+COLOR_SHIFT_G-16)) - #else // COLOR_BITS_G + COLOR_SHIFT_G < 16 - #define HTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) >> (16-(COLOR_BITS_G+COLOR_SHIFT_G))) - #endif - #if COLOR_BITS_B + COLOR_SHIFT_B == 8 - #define HTML2COLOR_B(h) ((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) - #elif COLOR_BITS_B + COLOR_SHIFT_B > 8 - #define HTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) << (COLOR_BITS_B+COLOR_SHIFT_B-8)) - #else // COLOR_BITS_B + COLOR_SHIFT_B < 8 - #define HTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) >> (8-(COLOR_BITS_B+COLOR_SHIFT_B))) - #endif - #define HTML2COLOR(h) ((COLOR_TYPE)(HTML2COLOR_R(h) | HTML2COLOR_G(h) | HTML2COLOR_B(h))) - -//------------------------- -// Gray-scale color system -//------------------------- -#elif (GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_GRAYSCALE - #define COLOR_SYSTEM GDISP_COLORSYSTEM_GRAYSCALE - - // Calculate the number of bits and shifts - #define COLOR_BITS (GDISP_PIXELFORMAT & 0xFF) - #define COLOR_BITS_R COLOR_BITS - #define COLOR_BITS_G COLOR_BITS - #define COLOR_BITS_B COLOR_BITS - #define COLOR_SHIFT_R 0 - #define COLOR_SHIFT_G 0 - #define COLOR_SHIFT_B 0 - - // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking - #if COLOR_BITS <= 8 - #define COLOR_TYPE uint8_t - #define COLOR_TYPE_BITS 8 - #else - #error "GDISP: Cannot define gray-scale color types with more than 8 bits" - #endif - #if COLOR_TYPE_BITS == COLOR_BITS - #define COLOR_NEEDS_MASK FALSE - #else - #define COLOR_NEEDS_MASK TRUE - #endif - #define COLOR_MASK() ((1 << COLOR_BITS)-1) - - #if COLOR_BITS == 1 - #define RGB2COLOR(r,g,b) (((r)|(g)|(b)) ? 1 : 0) - #define LUMA2COLOR(l) ((l) ? 1 : 0) - #define HTML2COLOR(h) ((h) ? 1 : 0) - #define LUMA_OF(c) ((c) ? 255 : 0) - #define EXACT_LUMA_OF(c) LUMA_OF(c) - #else - // They eye is more sensitive to green - #define RGB2COLOR(r,g,b) ((COLOR_TYPE)(((uint16_t)(r)+(g)+(g)+(b)) >> (10-COLOR_BITS))) - #define LUMA2COLOR(l) ((COLOR_TYPE)((l)>>(8-COLOR_BITS))) - #define HTML2COLOR(h) ((COLOR_TYPE)(((((h)&0xFF0000)>>16)+(((h)&0x00FF00)>>7)+((h)&0x0000FF)) >> (10-COLOR_BITS))) - #define LUMA_OF(c) (((c) & ((1<next) { - if (matchfont(name, fp->font->full_name)) - return fp->font; - } - - // Try the short names if no long names match - for(fp = mf_get_font_list(); fp; fp = fp->next) { - if (matchfont(name, fp->font->short_name)) - return fp->font; - } - - /* Return default font.. better than nothing. */ - return mf_get_font_list()->font; -} - -void gdispCloseFont(font_t font) { - if (font->flags & FONT_FLAG_DYNAMIC) - { - struct mf_font_s *dfont = (struct mf_font_s *)font; - - /* Make sure that no-one can successfully use font after closing */ - dfont->render_character = 0; - - /* Release the allocated memory */ - gfxFree(dfont); - } -} - -font_t gdispScaleFont(font_t font, uint8_t scale_x, uint8_t scale_y) -{ - struct mf_scaledfont_s *newfont = gfxAlloc(sizeof(struct mf_scaledfont_s)); - mf_scale_font(newfont, font, scale_x, scale_y); - return (font_t)newfont; -} - -const char *gdispGetFontName(font_t font) { - return font->short_name; -} - -#endif /* GFX_USE_GDISP && GDISP_NEED_TEXT */ -/** @} */ diff --git a/src/gdisp/fonts/build_fonts.sh b/src/gdisp/fonts/build_fonts.sh index 4ce93a40..b40f7fb6 100644 --- a/src/gdisp/fonts/build_fonts.sh +++ b/src/gdisp/fonts/build_fonts.sh @@ -72,7 +72,7 @@ for file in *.c; do defname='GDISP_INCLUDE_FONT_'$upper echo '#if defined('$defname') && '$defname >> fonts.h echo '#define GDISP_FONT_FOUND' >> fonts.h - echo '#include "src/gdisp/fonts/'$file'"' >> fonts.h + echo '#include "'$file'"' >> fonts.h echo '#endif' >> fonts.h done diff --git a/src/gdisp/fonts/fonts.h b/src/gdisp/fonts/fonts.h index af851dbd..253d0e2d 100644 --- a/src/gdisp/fonts/fonts.h +++ b/src/gdisp/fonts/fonts.h @@ -3,87 +3,87 @@ #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS10) && GDISP_INCLUDE_FONT_DEJAVUSANS10 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans10.c" +#include "DejaVuSans10.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS12_AA) && GDISP_INCLUDE_FONT_DEJAVUSANS12_AA #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans12_aa.c" +#include "DejaVuSans12_aa.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS12) && GDISP_INCLUDE_FONT_DEJAVUSANS12 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans12.c" +#include "DejaVuSans12.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS16_AA) && GDISP_INCLUDE_FONT_DEJAVUSANS16_AA #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans16_aa.c" +#include "DejaVuSans16_aa.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS16) && GDISP_INCLUDE_FONT_DEJAVUSANS16 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans16.c" +#include "DejaVuSans16.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS24_AA) && GDISP_INCLUDE_FONT_DEJAVUSANS24_AA #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans24_aa.c" +#include "DejaVuSans24_aa.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS24) && GDISP_INCLUDE_FONT_DEJAVUSANS24 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans24.c" +#include "DejaVuSans24.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS32_AA) && GDISP_INCLUDE_FONT_DEJAVUSANS32_AA #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans32_aa.c" +#include "DejaVuSans32_aa.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS32) && GDISP_INCLUDE_FONT_DEJAVUSANS32 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans32.c" +#include "DejaVuSans32.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12_AA) && GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12_AA #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSansBold12_aa.c" +#include "DejaVuSansBold12_aa.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12) && GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSansBold12.c" +#include "DejaVuSansBold12.c" #endif #if defined(GDISP_INCLUDE_FONT_FIXED_10X20) && GDISP_INCLUDE_FONT_FIXED_10X20 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/fixed_10x20.c" +#include "fixed_10x20.c" #endif #if defined(GDISP_INCLUDE_FONT_FIXED_5X8) && GDISP_INCLUDE_FONT_FIXED_5X8 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/fixed_5x8.c" +#include "fixed_5x8.c" #endif #if defined(GDISP_INCLUDE_FONT_FIXED_7X14) && GDISP_INCLUDE_FONT_FIXED_7X14 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/fixed_7x14.c" +#include "fixed_7x14.c" #endif #if defined(GDISP_INCLUDE_FONT_LARGENUMBERS) && GDISP_INCLUDE_FONT_LARGENUMBERS #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/LargeNumbers.c" +#include "LargeNumbers.c" #endif #if defined(GDISP_INCLUDE_FONT_UI1) && GDISP_INCLUDE_FONT_UI1 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/UI1.c" +#include "UI1.c" #endif #if defined(GDISP_INCLUDE_FONT_UI2) && GDISP_INCLUDE_FONT_UI2 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/UI2.c" +#include "UI2.c" #endif #if defined(GDISP_INCLUDE_USER_FONTS) && GDISP_INCLUDE_USER_FONTS diff --git a/src/gdisp/gdisp.c b/src/gdisp/gdisp.c deleted file mode 100644 index b8b4a847..00000000 --- a/src/gdisp/gdisp.c +++ /dev/null @@ -1,3100 +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.c - * @brief GDISP Driver code. - * - * @addtogroup GDISP - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GDISP - -/* Include the low level driver information */ -#include "src/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 - -// The color to clear the display on startup -#define GDISP_STARTUP_COLOR Black - -/*===========================================================================*/ -/* Driver local variables. */ -/*===========================================================================*/ - -// The controller array, the display array and the default display -#if GDISP_TOTAL_CONTROLLERS > 1 - typedef const struct GDISPVMT const VMTEL[1]; - extern VMTEL GDISP_CONTROLLER_LIST; - static const struct GDISPVMT const * ControllerList[GDISP_TOTAL_CONTROLLERS] = {GDISP_CONTROLLER_LIST}; - static const unsigned DisplayCountList[GDISP_TOTAL_CONTROLLERS] = {GDISP_CONTROLLER_DISPLAYS}; -#endif - -#if GDISP_NEED_TIMERFLUSH - static GTimer FlushTimer; -#endif - -static GDisplay GDisplayArray[GDISP_TOTAL_DISPLAYS]; -GDisplay *GDISP = GDisplayArray; - -#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) -#else - #define MUTEX_INIT(g) - #define MUTEX_ENTER(g) - #define MUTEX_EXIT(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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 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 = GDisplayArray; g < &GDisplayArray[GDISP_TOTAL_DISPLAYS]; g++) - gdispGFlush(g); - } -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -void _gdispInit(void) -{ - GDisplay *g; - uint16_t i; - - /* Initialise all controllers */ - #if GDISP_TOTAL_CONTROLLERS > 1 - uint16_t j; - - for(g = GDisplayArray, j=0; j < GDISP_TOTAL_CONTROLLERS; j++) - for(i = 0; i < DisplayCountList[j]; g++, i++) { - g->vmt = ControllerList[j]; - g->systemdisplay = j*GDISP_TOTAL_CONTROLLERS+i; - g->controllerdisplay = i; - #else - for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { - g->systemdisplay = i; - g->controllerdisplay = i; - #endif - MUTEX_INIT(g); - MUTEX_ENTER(g); - g->flags = 0; - gdisp_lld_init(g); - MUTEX_EXIT(g); - } - - // Set the orientation, the clipping area, clear all the displays (and add the logo if required) - for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { - #if defined(GDISP_DEFAULT_ORIENTATION) && GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL - gdispGControl(g, GDISP_CONTROL_ORIENTATION, (void *)GDISP_DEFAULT_ORIENTATION); - #endif - #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP - gdispGSetClip(g, 0, 0, g->g.Width, g->g.Height); - #endif - gdispGClear(g, GDISP_STARTUP_COLOR); - #if GDISP_STARTUP_LOGO_TIMEOUT > 0 - StartupLogoDisplay(g); - #endif - #if GDISP_HARDWARE_FLUSH - gdispGFlush(g); - #endif - } - - // Re-clear the display after the timeout if we added the logo - #if GDISP_STARTUP_LOGO_TIMEOUT > 0 - gfxSleepMilliseconds(GDISP_STARTUP_LOGO_TIMEOUT); - for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { - gdispGClear(g, GDISP_STARTUP_COLOR); - #if GDISP_HARDWARE_FLUSH - gdispGFlush(g); - #endif - } - #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 */ -} - -GDisplay *gdispGetDisplay(unsigned display) { - if (display >= GDISP_TOTAL_DISPLAYS) - return 0; - return &GDisplayArray[display]; -} - -void gdispSetDisplay(GDisplay *g) { - if (g) GDISP = g; -} - -void gdispGFlush(GDisplay *g) { - #if GDISP_HARDWARE_FLUSH - #if GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT - if (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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_ARC - #if !GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG - #include - #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 - 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; - } - gdispGDrawArc(g, x+radius, y+radius, radius, 90, 180, color); - gdispGDrawLine(g, x+radius+1, y, x+cx-2-radius, y, color); - gdispGDrawArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color); - gdispGDrawLine(g, x+cx-1, y+radius+1, x+cx-1, y+cy-2-radius, color); - gdispGDrawArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); - gdispGDrawLine(g, x+radius+1, y+cy-1, x+cx-2-radius, y+cy-1, color); - gdispGDrawArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); - gdispGDrawLine(g, x, y+radius+1, x, y+cy-2-radius, color); - } -#endif - -#if GDISP_NEED_ARC - 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; - } - gdispGFillArc(g, x+radius, y+radius, radius, 90, 180, color); - gdispGFillArea(g, x+radius+1, y, cx-radius2, radius, 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); - gdispGFillArea(g, x+radius+1, y+cy-radius, cx-radius2, radius, color); - gdispGFillArc(g, x+radius, y+cy-1-radius, radius, 180, 270, 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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (!g->vmt->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.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) { - /* 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 - -#if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT - 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 - } -#endif - -#if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT - 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 /* GFX_USE_GDISP */ -/** @} */ diff --git a/src/gdisp/gdisp_colors.h b/src/gdisp/gdisp_colors.h new file mode 100644 index 00000000..b764ccfe --- /dev/null +++ b/src/gdisp/gdisp_colors.h @@ -0,0 +1,377 @@ +/* + * 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_colors.h + * @brief GDISP color definitions header file. + * + * @defgroup Colors Colors + * @ingroup GDISP + * @{ + */ + +#ifndef _GDISP_COLORS_H +#define _GDISP_COLORS_H + +#include "gfx.h" + +#if GFX_USE_GDISP || defined(__DOXYGEN__) + +/** + * For pixel formats we do some assignment of codes to enable + * format auto-calculation. (Undocumented). + * 0x2RGB TRUECOLOR RGB format, R = red bits, G = green bits, B = blue bits + * 0x3RGB TRUECOLOR BGR format, R = red bits, G = green bits, B = blue bits + * 0x40XX GRAYSCALE XX = bits + * 0x60XX PALLETTE XX = bits + * 0x8XXX CUSTOM format. + */ +#define GDISP_COLORSYSTEM_MASK 0xF000 +#define GDISP_COLORSYSTEM_RGB 0x2000 +#define GDISP_COLORSYSTEM_BGR 0x3000 + +/** + * @brief Color Type Constants + * @{ + */ +#define GDISP_COLORSYSTEM_TRUECOLOR 0x2000 +#define GDISP_COLORSYSTEM_GRAYSCALE 0x4000 +#define GDISP_COLORSYSTEM_PALETTE 0x6000 +/** @} */ + +/** + * @brief Pixel Format Constants + * @{ + */ +#define GDISP_PIXELFORMAT_MONO (GDISP_COLORSYSTEM_GRAYSCALE|0x0001) +#define GDISP_PIXELFORMAT_GRAY4 (GDISP_COLORSYSTEM_GRAYSCALE|0x0002) +#define GDISP_PIXELFORMAT_GRAY16 (GDISP_COLORSYSTEM_GRAYSCALE|0x0004) +#define GDISP_PIXELFORMAT_GRAY256 (GDISP_COLORSYSTEM_GRAYSCALE|0x0008) +#define GDISP_PIXELFORMAT_RGB565 (GDISP_COLORSYSTEM_RGB|0x0565) +#define GDISP_PIXELFORMAT_BGR565 (GDISP_COLORSYSTEM_BGR|0x0565) +#define GDISP_PIXELFORMAT_RGB888 (GDISP_COLORSYSTEM_RGB|0x0888) +#define GDISP_PIXELFORMAT_BGR888 (GDISP_COLORSYSTEM_BGR|0x0888) +#define GDISP_PIXELFORMAT_RGB444 (GDISP_COLORSYSTEM_RGB|0x0444) +#define GDISP_PIXELFORMAT_BGR444 (GDISP_COLORSYSTEM_BGR|0x0444) +#define GDISP_PIXELFORMAT_RGB332 (GDISP_COLORSYSTEM_RGB|0x0332) +#define GDISP_PIXELFORMAT_BGR332 (GDISP_COLORSYSTEM_BGR|0x0332) +#define GDISP_PIXELFORMAT_RGB666 (GDISP_COLORSYSTEM_RGB|0x0666) +#define GDISP_PIXELFORMAT_BGR666 (GDISP_COLORSYSTEM_BGR|0x0666) +#define GDISP_PIXELFORMAT_ERROR 0x0000 +/** @} */ + +/** + * @name Some basic colors + * @{ + */ +#define White HTML2COLOR(0xFFFFFF) +#define Black HTML2COLOR(0x000000) +#define Gray HTML2COLOR(0x808080) +#define Grey Gray +#define Blue HTML2COLOR(0x0000FF) +#define Red HTML2COLOR(0xFF0000) +#define Fuchsia HTML2COLOR(0xFF00FF) +#define Magenta Fuchsia +#define Green HTML2COLOR(0x008000) +#define Yellow HTML2COLOR(0xFFFF00) +#define Aqua HTML2COLOR(0x00FFFF) +#define Cyan Aqua +#define Lime HTML2COLOR(0x00FF00) +#define Maroon HTML2COLOR(0x800000) +#define Navy HTML2COLOR(0x000080) +#define Olive HTML2COLOR(0x808000) +#define Purple HTML2COLOR(0x800080) +#define Silver HTML2COLOR(0xC0C0C0) +#define Teal HTML2COLOR(0x008080) +#define Orange HTML2COLOR(0xFFA500) +#define Pink HTML2COLOR(0xFFC0CB) +#define SkyBlue HTML2COLOR(0x87CEEB) +/** @} */ + +#if defined(__DOXYGEN__) + /** + * @brief The color system (grayscale, palette or truecolor) + */ + #define COLOR_SYSTEM GDISP_COLORSYSTEM_TRUECOLOR + /** + * @brief The number of bits in a color value + */ + #define COLOR_BITS 16 + /** + * @brief The number of bits for each of red, green and blue + * @{ + */ + #define COLOR_BITS_R 5 + #define COLOR_BITS_G 6 + #define COLOR_BITS_B 5 + /** @} */ + /** + * @brief The number of bits to shift each of red, green and blue to put it in the correct place in the color + * @{ + */ + #define COLOR_SHIFT_R 11 + #define COLOR_SHIFT_G 5 + #define COLOR_SHIFT_B 0 + /** @} */ + /** + * @brief Does the color need masking to remove invalid bits + */ + #define COLOR_NEEDS_MASK FALSE + /** + * @brief If the color needs masking to remove invalid bits, this is the mask + */ + #define COLOR_MASK 0xFFFF + /** + * @brief The color type + * @{ + */ + #define COLOR_TYPE uint16_t + /** @} */ + /** + * @brief The number of bits in the color type (not necessarily the same as COLOR_BITS). + */ + #define COLOR_TYPE_BITS 16 + /** + * @brief Convert a luminance (0 to 255) into a color value. + * @note The word "Luma" is used instead of grey or gray due to the spelling ambiguities of the word grey + * @note This is not a weighted luminance conversion in the color tv style. + * @note @p LUMA2COLOR() uses a linear conversion (0.33R + 0.33G + 0.33B). Note this is different to color + * tv luminance (0.26126R + 0.7152G + 0.0722B), digital tv luminance of (0.299R + 0.587G + 0.114B), or + * @p LUMA_OF() which uses (0.25R + 0.5G + 0.25B). + */ + #define LUMA2COLOR(l) ((color_t)((((l) & 0xF8)<<8) | (((l) & 0xFC)<<3) | (((l) & 0xF8)>>3))) + /** + * @brief Convert red, green, blue (each 0 to 255) into a color value. + */ + #define RGB2COLOR(r,g,b) ((color_t)((((r) & 0xF8)<<8) | (((g) & 0xFC)<<3) | (((b) & 0xF8)>>3))) + /** + * @brief Convert a 6 digit HTML code (hex) into a color value. + */ + #define HTML2COLOR(h) ((color_t)((((h) & 0xF80000)>>8) | (((h) & 0x00FC00)>>5) | (((h) & 0x0000F8)>>3))) + /** + * @brief Extract the luma/red/green/blue component (0 to 255) of a color value. + * @note This uses quick and dirty bit shifting. If you want more exact colors + * use @p EXACT_RED_OF() etc which uses multiplies and divides. For constant + * colors using @p EXACT_RED_OF() is no more expensive because the compiler + * evaluates the arithmetic. + * @note @p LUMA_OF() returns a roughly weighted luminance (0.25R + 0.5G + 0.25B). Note this is + * different to @p LUMA2COLOR() which uses a linear conversion (0.33R + 0.33G + 0.33B) and + * color tv luminance of (0.26126R + 0.7152G + 0.0722B) and digital tv luminance of (0.299R + 0.587G + 0.114B). + * @note A 5 bit color component maximum value (0x1F) converts to 0xF8 (slightly off-color) + * @{ + */ + #define LUMA_OF(c) ((RED_OF(c)+((uint16_t)GREEN_OF(c)<<1)+BLUE_OF(c))>>2) + #define RED_OF(c) (((c) & 0xF800)>>8) + #define GREEN_OF(c) (((c)&0x007E)>>3) + #define BLUE_OF(c) (((c)&0x001F)<<3) + /** @} */ + /** + * @brief Extract the exact luma/red/green/blue component (0 to 255) of a color value. + * @note This uses multiplies and divides rather than bit shifting. + * This gives exact equivalent colors at the expense of more cpu intensive + * operations. Note for constants this is no more expensive than @p REF_OF() + * because the compiler evaluates the arithmetic. + * @note @p EXACT_LUMA_OF() returns a roughly weighted luminance (0.25R + 0.5G + 0.25B). Note this is + * different to @p LUMA2COLOR() which uses a linear conversion (0.33R + 0.33G + 0.33B) and + * color tv luminance of (0.26126R + 0.7152G + 0.0722B) and digital tv luminance of (0.299R + 0.587G + 0.114B). + * @note A 5 bit color component maximum value (0x1F) converts to 0xFF (the true equivalent color) + * @{ + */ + #define EXACT_LUMA_OF(c) ((EXACT_RED_OF(c)+((uint16_t)EXACT_GREEN_OF(c)<<1)+EXACT_BLUE_OF(c))>>2) + #define EXACT_RED_OF(c) (((((c)>>11)&0x1F)*255)/31) + #define EXACT_GREEN_OF(c) (((((c)>>5)&0x3F)*255)/63) + #define EXACT_BLUE_OF(c) (((((c)>>0)&0x1F)*255)/31) + /** @} */ +#endif + +/* + * We use this big mess of macros to calculate all the components + * to prevent user errors in the color definitions. It greatly simplifies + * the above definitions and ensures a consistent implementation. + */ + +//------------------------- +// True-Color color system +//------------------------- +#if GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_TRUECOLOR + #define COLOR_SYSTEM GDISP_COLORSYSTEM_TRUECOLOR + + // Calculate the number of bits + #define COLOR_BITS_R ((GDISP_PIXELFORMAT>>8) & 0x0F) + #define COLOR_BITS_G ((GDISP_PIXELFORMAT>>4) & 0x0F) + #define COLOR_BITS_B ((GDISP_PIXELFORMAT>>0) & 0x0F) + #define COLOR_BITS (COLOR_BITS_R + COLOR_BITS_G + COLOR_BITS_B) + + // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking + #if COLOR_BITS <= 8 + #define COLOR_TYPE uint8_t + #define COLOR_TYPE_BITS 8 + #elif COLOR_BITS <= 16 + #define COLOR_TYPE uint16_t + #define COLOR_TYPE_BITS 16 + #elif COLOR_BITS <= 32 + #define COLOR_TYPE uint32_t + #define COLOR_TYPE_BITS 32 + #else + #error "GDISP: Cannot define color types with more than 32 bits" + #endif + #if COLOR_TYPE_BITS == COLOR_BITS + #define COLOR_NEEDS_MASK FALSE + #else + #define COLOR_NEEDS_MASK TRUE + #endif + #define COLOR_MASK() ((1 << COLOR_BITS)-1) + + // Calculate the component bit shifts + #if (GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_RGB + #define COLOR_SHIFT_R (COLOR_BITS_B+COLOR_BITS_G) + #define COLOR_SHIFT_G COLOR_BITS_B + #define COLOR_SHIFT_B 0 + #else + #define COLOR_SHIFT_B (COLOR_BITS_R+COLOR_BITS_G) + #define COLOR_SHIFT_G COLOR_BITS_R + #define COLOR_SHIFT_R 0 + #endif + + // Calculate RED_OF, GREEN_OF, BLUE_OF and RGB2COLOR + #if COLOR_BITS_R + COLOR_SHIFT_R == 8 + #define RED_OF(c) ((c) & (((1< 8 + #define RED_OF(c) (((c) & (((1<> (COLOR_BITS_R+COLOR_SHIFT_R-8)) + #define RGB2COLOR_R(r) (((COLOR_TYPE)((r) & (0xFF & ~((1<<(8-COLOR_BITS_R))-1)))) << (COLOR_BITS_R+COLOR_SHIFT_R-8)) + #else // COLOR_BITS_R + COLOR_SHIFT_R < 8 + #define RED_OF(c) (((c) & (((1<> (8-(COLOR_BITS_R+COLOR_SHIFT_R))) + #endif + #if COLOR_BITS_G + COLOR_SHIFT_G == 8 + #define GREEN_OF(c) ((c) & (((1< 8 + #define GREEN_OF(c) (((c) & (((1<> (COLOR_BITS_G+COLOR_SHIFT_G-8)) + #define RGB2COLOR_G(g) (((COLOR_TYPE)((g) & (0xFF & ~((1<<(8-COLOR_BITS_G))-1)))) << (COLOR_BITS_G+COLOR_SHIFT_G-8)) + #else // COLOR_BITS_G + COLOR_SHIFT_G < 8 + #define GREEN_OF(c) (((c) & (((1<> (8-(COLOR_BITS_G+COLOR_SHIFT_G))) + #endif + #if COLOR_BITS_B + COLOR_SHIFT_B == 8 + #define BLUE_OF(c) ((c) & (((1< 8 + #define BLUE_OF(c) (((c) & (((1<> (COLOR_BITS_B+COLOR_SHIFT_B-8)) + #define RGB2COLOR_B(b) (((COLOR_TYPE)((b) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1)))) << (COLOR_BITS_B+COLOR_SHIFT_B-8)) + #else // COLOR_BITS_B + COLOR_SHIFT_B < 8 + #define BLUE_OF(c) (((c) & (((1<> (8-(COLOR_BITS_B+COLOR_SHIFT_B))) + #endif + #define LUMA_OF(c) ((RED_OF(c)+((uint16_t)GREEN_OF(c)<<1)+BLUE_OF(c))>>2) + #define EXACT_RED_OF(c) (((uint16_t)(((c)>>COLOR_SHIFT_R)&((1<>COLOR_SHIFT_G)&((1<>COLOR_SHIFT_B)&((1<>2) + #define LUMA2COLOR(l) (RGB2COLOR_R(l) | RGB2COLOR_G(l) | RGB2COLOR_B(l)) + #define RGB2COLOR(r,g,b) (RGB2COLOR_R(r) | RGB2COLOR_G(g) | RGB2COLOR_B(b)) + + // Calculate HTML2COLOR + #if COLOR_BITS_R + COLOR_SHIFT_R == 24 + #define HTML2COLOR_R(h) ((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) + #elif COLOR_BITS_R + COLOR_SHIFT_R > 24 + #define HTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) << (COLOR_BITS_R+COLOR_SHIFT_R-24)) + #else // COLOR_BITS_R + COLOR_SHIFT_R < 24 + #define HTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) >> (24-(COLOR_BITS_R+COLOR_SHIFT_R))) + #endif + #if COLOR_BITS_G + COLOR_SHIFT_G == 16 + #define HTML2COLOR_G(h) ((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) + #elif COLOR_BITS_G + COLOR_SHIFT_G > 16 + #define HTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) << (COLOR_BITS_G+COLOR_SHIFT_G-16)) + #else // COLOR_BITS_G + COLOR_SHIFT_G < 16 + #define HTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) >> (16-(COLOR_BITS_G+COLOR_SHIFT_G))) + #endif + #if COLOR_BITS_B + COLOR_SHIFT_B == 8 + #define HTML2COLOR_B(h) ((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) + #elif COLOR_BITS_B + COLOR_SHIFT_B > 8 + #define HTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) << (COLOR_BITS_B+COLOR_SHIFT_B-8)) + #else // COLOR_BITS_B + COLOR_SHIFT_B < 8 + #define HTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) >> (8-(COLOR_BITS_B+COLOR_SHIFT_B))) + #endif + #define HTML2COLOR(h) ((COLOR_TYPE)(HTML2COLOR_R(h) | HTML2COLOR_G(h) | HTML2COLOR_B(h))) + +//------------------------- +// Gray-scale color system +//------------------------- +#elif (GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_GRAYSCALE + #define COLOR_SYSTEM GDISP_COLORSYSTEM_GRAYSCALE + + // Calculate the number of bits and shifts + #define COLOR_BITS (GDISP_PIXELFORMAT & 0xFF) + #define COLOR_BITS_R COLOR_BITS + #define COLOR_BITS_G COLOR_BITS + #define COLOR_BITS_B COLOR_BITS + #define COLOR_SHIFT_R 0 + #define COLOR_SHIFT_G 0 + #define COLOR_SHIFT_B 0 + + // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking + #if COLOR_BITS <= 8 + #define COLOR_TYPE uint8_t + #define COLOR_TYPE_BITS 8 + #else + #error "GDISP: Cannot define gray-scale color types with more than 8 bits" + #endif + #if COLOR_TYPE_BITS == COLOR_BITS + #define COLOR_NEEDS_MASK FALSE + #else + #define COLOR_NEEDS_MASK TRUE + #endif + #define COLOR_MASK() ((1 << COLOR_BITS)-1) + + #if COLOR_BITS == 1 + #define RGB2COLOR(r,g,b) (((r)|(g)|(b)) ? 1 : 0) + #define LUMA2COLOR(l) ((l) ? 1 : 0) + #define HTML2COLOR(h) ((h) ? 1 : 0) + #define LUMA_OF(c) ((c) ? 255 : 0) + #define EXACT_LUMA_OF(c) LUMA_OF(c) + #else + // They eye is more sensitive to green + #define RGB2COLOR(r,g,b) ((COLOR_TYPE)(((uint16_t)(r)+(g)+(g)+(b)) >> (10-COLOR_BITS))) + #define LUMA2COLOR(l) ((COLOR_TYPE)((l)>>(8-COLOR_BITS))) + #define HTML2COLOR(h) ((COLOR_TYPE)(((((h)&0xFF0000)>>16)+(((h)&0x00FF00)>>7)+((h)&0x0000FF)) >> (10-COLOR_BITS))) + #define LUMA_OF(c) (((c) & ((1<next) { + if (matchfont(name, fp->font->full_name)) + return fp->font; + } + + // Try the short names if no long names match + for(fp = mf_get_font_list(); fp; fp = fp->next) { + if (matchfont(name, fp->font->short_name)) + return fp->font; + } + + /* Return default font.. better than nothing. */ + return mf_get_font_list()->font; +} + +void gdispCloseFont(font_t font) { + if (font->flags & FONT_FLAG_DYNAMIC) + { + struct mf_font_s *dfont = (struct mf_font_s *)font; + + /* Make sure that no-one can successfully use font after closing */ + dfont->render_character = 0; + + /* Release the allocated memory */ + gfxFree(dfont); + } +} + +font_t gdispScaleFont(font_t font, uint8_t scale_x, uint8_t scale_y) +{ + struct mf_scaledfont_s *newfont = gfxAlloc(sizeof(struct mf_scaledfont_s)); + mf_scale_font(newfont, font, scale_x, scale_y); + return (font_t)newfont; +} + +const char *gdispGetFontName(font_t font) { + return font->short_name; +} + +#endif /* GFX_USE_GDISP && GDISP_NEED_TEXT */ +/** @} */ diff --git a/src/gdisp/gdisp_gdisp.c b/src/gdisp/gdisp_gdisp.c new file mode 100644 index 00000000..02ee4711 --- /dev/null +++ b/src/gdisp/gdisp_gdisp.c @@ -0,0 +1,3100 @@ +/* + * 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 + +// The color to clear the display on startup +#define GDISP_STARTUP_COLOR Black + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +// The controller array, the display array and the default display +#if GDISP_TOTAL_CONTROLLERS > 1 + typedef const struct GDISPVMT const VMTEL[1]; + extern VMTEL GDISP_CONTROLLER_LIST; + static const struct GDISPVMT const * ControllerList[GDISP_TOTAL_CONTROLLERS] = {GDISP_CONTROLLER_LIST}; + static const unsigned DisplayCountList[GDISP_TOTAL_CONTROLLERS] = {GDISP_CONTROLLER_DISPLAYS}; +#endif + +#if GDISP_NEED_TIMERFLUSH + static GTimer FlushTimer; +#endif + +static GDisplay GDisplayArray[GDISP_TOTAL_DISPLAYS]; +GDisplay *GDISP = GDisplayArray; + +#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) +#else + #define MUTEX_INIT(g) + #define MUTEX_ENTER(g) + #define MUTEX_EXIT(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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 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 = GDisplayArray; g < &GDisplayArray[GDISP_TOTAL_DISPLAYS]; g++) + gdispGFlush(g); + } +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +void _gdispInit(void) +{ + GDisplay *g; + uint16_t i; + + /* Initialise all controllers */ + #if GDISP_TOTAL_CONTROLLERS > 1 + uint16_t j; + + for(g = GDisplayArray, j=0; j < GDISP_TOTAL_CONTROLLERS; j++) + for(i = 0; i < DisplayCountList[j]; g++, i++) { + g->vmt = ControllerList[j]; + g->systemdisplay = j*GDISP_TOTAL_CONTROLLERS+i; + g->controllerdisplay = i; + #else + for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { + g->systemdisplay = i; + g->controllerdisplay = i; + #endif + MUTEX_INIT(g); + MUTEX_ENTER(g); + g->flags = 0; + gdisp_lld_init(g); + MUTEX_EXIT(g); + } + + // Set the orientation, the clipping area, clear all the displays (and add the logo if required) + for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { + #if defined(GDISP_DEFAULT_ORIENTATION) && GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL + gdispGControl(g, GDISP_CONTROL_ORIENTATION, (void *)GDISP_DEFAULT_ORIENTATION); + #endif + #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP + gdispGSetClip(g, 0, 0, g->g.Width, g->g.Height); + #endif + gdispGClear(g, GDISP_STARTUP_COLOR); + #if GDISP_STARTUP_LOGO_TIMEOUT > 0 + StartupLogoDisplay(g); + #endif + #if GDISP_HARDWARE_FLUSH + gdispGFlush(g); + #endif + } + + // Re-clear the display after the timeout if we added the logo + #if GDISP_STARTUP_LOGO_TIMEOUT > 0 + gfxSleepMilliseconds(GDISP_STARTUP_LOGO_TIMEOUT); + for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { + gdispGClear(g, GDISP_STARTUP_COLOR); + #if GDISP_HARDWARE_FLUSH + gdispGFlush(g); + #endif + } + #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 */ +} + +GDisplay *gdispGetDisplay(unsigned display) { + if (display >= GDISP_TOTAL_DISPLAYS) + return 0; + return &GDisplayArray[display]; +} + +void gdispSetDisplay(GDisplay *g) { + if (g) GDISP = g; +} + +void gdispGFlush(GDisplay *g) { + #if GDISP_HARDWARE_FLUSH + #if GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT + if (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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_ARC + #if !GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG + #include + #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 + 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; + } + gdispGDrawArc(g, x+radius, y+radius, radius, 90, 180, color); + gdispGDrawLine(g, x+radius+1, y, x+cx-2-radius, y, color); + gdispGDrawArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color); + gdispGDrawLine(g, x+cx-1, y+radius+1, x+cx-1, y+cy-2-radius, color); + gdispGDrawArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); + gdispGDrawLine(g, x+radius+1, y+cy-1, x+cx-2-radius, y+cy-1, color); + gdispGDrawArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); + gdispGDrawLine(g, x, y+radius+1, x, y+cy-2-radius, color); + } +#endif + +#if GDISP_NEED_ARC + 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; + } + gdispGFillArc(g, x+radius, y+radius, radius, 90, 180, color); + gdispGFillArea(g, x+radius+1, y, cx-radius2, radius, 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); + gdispGFillArea(g, x+radius+1, y+cy-radius, cx-radius2, radius, color); + gdispGFillArc(g, x+radius, y+cy-1-radius, radius, 180, 270, 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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (g->vmt->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 (!g->vmt->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 (g->vmt->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 (!g->vmt->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) { + /* 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 + +#if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT + 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 + } +#endif + +#if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT + 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 /* GFX_USE_GDISP */ +/** @} */ diff --git a/src/gdisp/gdisp_image.c b/src/gdisp/gdisp_image.c new file mode 100644 index 00000000..2851a7c8 --- /dev/null +++ b/src/gdisp/gdisp_image.c @@ -0,0 +1,233 @@ +/* + * 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_image.c + * @brief GDISP generic image code. + * + * @defgroup Image Image + * @ingroup GDISP + */ +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE + +#if GDISP_NEED_IMAGE_NATIVE + extern gdispImageError gdispImageOpen_NATIVE(gdispImage *img); + extern void gdispImageClose_NATIVE(gdispImage *img); + extern gdispImageError gdispImageCache_NATIVE(gdispImage *img); + extern gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_NATIVE(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_GIF + extern gdispImageError gdispImageOpen_GIF(gdispImage *img); + extern void gdispImageClose_GIF(gdispImage *img); + extern gdispImageError gdispImageCache_GIF(gdispImage *img); + extern gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_GIF(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_BMP + extern gdispImageError gdispImageOpen_BMP(gdispImage *img); + extern void gdispImageClose_BMP(gdispImage *img); + extern gdispImageError gdispImageCache_BMP(gdispImage *img); + extern gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_BMP(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_JPG + extern gdispImageError gdispImageOpen_JPG(gdispImage *img); + extern void gdispImageClose_JPG(gdispImage *img); + extern gdispImageError gdispImageCache_JPG(gdispImage *img); + extern gdispImageError gdispGImageDraw_JPG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_JPG(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_PNG + extern gdispImageError gdispImageOpen_PNG(gdispImage *img); + extern void gdispImageClose_PNG(gdispImage *img); + extern gdispImageError gdispImageCache_PNG(gdispImage *img); + extern gdispImageError gdispGImageDraw_PNG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_PNG(gdispImage *img); +#endif + +/* The structure defining the routines for image drawing */ +typedef struct gdispImageHandlers { + gdispImageError (*open)(gdispImage *img); /* The open function */ + void (*close)(gdispImage *img); /* The close function */ + gdispImageError (*cache)(gdispImage *img); /* The cache function */ + gdispImageError (*draw)(GDisplay *g, + gdispImage *img, + coord_t x, coord_t y, + coord_t cx, coord_t cy, + coord_t sx, coord_t sy); /* The draw function */ + delaytime_t (*next)(gdispImage *img); /* The next frame function */ +} gdispImageHandlers; + +static gdispImageHandlers ImageHandlers[] = { + #if GDISP_NEED_IMAGE_NATIVE + { gdispImageOpen_NATIVE, gdispImageClose_NATIVE, + gdispImageCache_NATIVE, gdispGImageDraw_NATIVE, gdispImageNext_NATIVE, + }, + #endif + #if GDISP_NEED_IMAGE_GIF + { gdispImageOpen_GIF, gdispImageClose_GIF, + gdispImageCache_GIF, gdispGImageDraw_GIF, gdispImageNext_GIF, + }, + #endif + #if GDISP_NEED_IMAGE_BMP + { gdispImageOpen_BMP, gdispImageClose_BMP, + gdispImageCache_BMP, gdispGImageDraw_BMP, gdispImageNext_BMP, + }, + #endif + #if GDISP_NEED_IMAGE_JPG + { gdispImageOpen_JPG, gdispImageClose_JPG, + gdispImageCache_JPG, gdispGImageDraw_JPG, gdispImageNext_JPG, + }, + #endif + #if GDISP_NEED_IMAGE_PNG + { gdispImageOpen_PNG, gdispImageClose_PNG, + gdispImageCache_PNG, gdispGImageDraw_PNG, gdispImageNext_PNG, + }, + #endif +}; + +gdispImageError + DEPRECATED("Use gdispImageOpenGFile() instead") + gdispImageOpen(gdispImage *img) { + return gdispImageOpenGFile(img, img->f); +} + +#if GFILE_NEED_MEMFS + bool_t + DEPRECATED("Use gdispImageOpenMemory() instead") + gdispImageSetMemoryReader(gdispImage *img, const void *memimage) { + img->f = gfileOpenMemory((void *)memimage, "rb"); + return img->f != 0; + } +#endif + +#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX + bool_t + DEPRECATED("Use gdispImageOpenFile() instead") + gdispImageSetFileReader(gdispImage *img, const char *filename) { + img->f = gfileOpen(filename, "rb"); + return img->f != 0; + } +#endif + +#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS + bool_t + DEPRECATED("Use gdispImageOpenBaseFileStream() instead") + gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr) { + img->f = gfileOpenBaseFileStream(BaseFileStreamPtr, "rb"); + return img->f != 0; + } +#endif + +void gdispImageInit(gdispImage *img) { + img->type = GDISP_IMAGE_TYPE_UNKNOWN; +} + +gdispImageError gdispImageOpenGFile(gdispImage *img, GFILE *f) { + gdispImageError err; + + if (!f) + return GDISP_IMAGE_ERR_NOSUCHFILE; + img->f = f; + img->bgcolor = White; + for(img->fns = ImageHandlers; img->fns < ImageHandlers+sizeof(ImageHandlers)/sizeof(ImageHandlers[0]); img->fns++) { + err = img->fns->open(img); + if (err != GDISP_IMAGE_ERR_BADFORMAT) { + if ((err & GDISP_IMAGE_ERR_UNRECOVERABLE)) + goto unrecoverable; + + // Everything is possible + return err; + } + + // Try the next decoder + gfileSetPos(img->f, 0); + } + + err = GDISP_IMAGE_ERR_BADFORMAT; + img->type = GDISP_IMAGE_TYPE_UNKNOWN; + +unrecoverable: + gfileClose(img->f); + img->f = 0; + img->flags = 0; + img->fns = 0; + img->priv = 0; + return err; +} + +void gdispImageClose(gdispImage *img) { + if (img->fns) + img->fns->close(img); + gfileClose(img->f); + img->type = GDISP_IMAGE_TYPE_UNKNOWN; + img->flags = 0; + img->fns = 0; + img->priv = 0; +} + +bool_t gdispImageIsOpen(gdispImage *img) { + return img->type != GDISP_IMAGE_TYPE_UNKNOWN && img->fns != 0; +} + +void gdispImageSetBgColor(gdispImage *img, color_t bgcolor) { + img->bgcolor = bgcolor; +} + +gdispImageError gdispImageCache(gdispImage *img) { + if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; + return img->fns->cache(img); +} + +gdispImageError gdispGImageDraw(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { + if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; + return img->fns->draw(g, img, x, y, cx, cy, sx, sy); +} + +delaytime_t gdispImageNext(gdispImage *img) { + if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; + return img->fns->next(img); +} + +// Helper Routines +void *gdispImageAlloc(gdispImage *img, size_t sz) { + #if GDISP_NEED_IMAGE_ACCOUNTING + void *ptr; + + ptr = gfxAlloc(sz); + if (ptr) { + img->memused += sz; + if (img->memused > img->maxmemused) + img->maxmemused = img->memused; + } + return ptr; + #else + (void) img; + return gfxAlloc(sz); + #endif +} + +void gdispImageFree(gdispImage *img, void *ptr, size_t sz) { + #if GDISP_NEED_IMAGE_ACCOUNTING + gfxFree(ptr); + img->memused -= sz; + #else + (void) img; + (void) sz; + gfxFree(ptr); + #endif +} + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ +/** @} */ diff --git a/src/gdisp/gdisp_image.h b/src/gdisp/gdisp_image.h new file mode 100644 index 00000000..c0941fd5 --- /dev/null +++ b/src/gdisp/gdisp_image.h @@ -0,0 +1,315 @@ +/* + * 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_image.h + * @brief GDISP image header file. + * + * @defgroup Image Image + * @ingroup GDISP + * @{ + */ + +#ifndef _GDISP_IMAGE_H +#define _GDISP_IMAGE_H +#if (GFX_USE_GDISP && GDISP_NEED_IMAGE) || defined(__DOXYGEN__) + +/** + * @brief The type of image + */ +typedef uint16_t gdispImageType; + #define GDISP_IMAGE_TYPE_UNKNOWN 0 + #define GDISP_IMAGE_TYPE_NATIVE 1 + #define GDISP_IMAGE_TYPE_GIF 2 + #define GDISP_IMAGE_TYPE_BMP 3 + #define GDISP_IMAGE_TYPE_JPG 4 + #define GDISP_IMAGE_TYPE_PNG 5 + +/** + * @brief An image error code + */ +typedef uint16_t gdispImageError; + #define GDISP_IMAGE_ERR_OK 0 + #define GDISP_IMAGE_ERR_UNRECOVERABLE 0x8000 + #define GDISP_IMAGE_ERR_BADFORMAT (GDISP_IMAGE_ERR_UNRECOVERABLE+1) + #define GDISP_IMAGE_ERR_BADDATA (GDISP_IMAGE_ERR_UNRECOVERABLE+2) + #define GDISP_IMAGE_ERR_UNSUPPORTED (GDISP_IMAGE_ERR_UNRECOVERABLE+3) + #define GDISP_IMAGE_ERR_UNSUPPORTED_OK 3 + #define GDISP_IMAGE_ERR_NOMEMORY (GDISP_IMAGE_ERR_UNRECOVERABLE+4) + #define GDISP_IMAGE_ERR_NOSUCHFILE (GDISP_IMAGE_ERR_UNRECOVERABLE+5) + +/** + * @brief Image flags + */ +typedef uint16_t gdispImageFlags; + #define GDISP_IMAGE_FLG_TRANSPARENT 0x0001 /* The image has transparency */ + #define GDISP_IMAGE_FLG_ANIMATED 0x0002 /* The image has animation */ + #define GDISP_IMAGE_FLG_MULTIPAGE 0x0004 /* The image has multiple pages */ + +struct gdispImageIO; + +/** + * @brief An image IO close function + * + * @param[in] pio Pointer to the io structure + * @param[in] desc The descriptor. A filename or an image structure pointer. + * + */ +typedef void (*gdispImageIOCloseFn)(struct gdispImageIO *pio); + +/** + * @brief An image IO read function + * @returns The number of bytes actually read or 0 on error + * + * @param[in] pio Pointer to the io structure + * @param[in] buf Where the results should be placed + * @param[in] len The number of bytes to read + * + */ +typedef size_t (*gdispImageIOReadFn)(struct gdispImageIO *pio, void *buf, size_t len); + +/** + * @brief An image IO seek function + * + * @param[in] pio Pointer to the io structure + * @param[in] pos Which byte to seek to relative to the start of the "file". + * + */ +typedef void (*gdispImageIOSeekFn)(struct gdispImageIO *pio, size_t pos); + +typedef struct gdispImageIOFunctions { + gdispImageIOReadFn read; /* @< The function to read input */ + gdispImageIOSeekFn seek; /* @< The function to seek input */ + gdispImageIOCloseFn close; /* @< The function to close input */ + } gdispImageIOFunctions; + +/** + * @brief The structure defining the IO routines for image handling + */ +typedef struct gdispImageIO { + const void * fd; /* @< The "file" descriptor */ + size_t pos; /* @< The current "file" position */ + const gdispImageIOFunctions *fns; /* @< The current "file" functions */ +} gdispImageIO; + +/** + * @brief The structure for an image + */ +typedef struct gdispImage { + gdispImageType type; /* @< The image type */ + gdispImageFlags flags; /* @< The image flags */ + color_t bgcolor; /* @< The default background color */ + coord_t width, height; /* @< The image dimensions */ + GFILE * f; /* @< The underlying GFILE */ + #if GDISP_NEED_IMAGE_ACCOUNTING + uint32_t memused; /* @< How much RAM is currently allocated */ + uint32_t maxmemused; /* @< How much RAM has been allocated (maximum) */ + #endif + const struct gdispImageHandlers * fns; /* @< Don't mess with this! */ + struct gdispImagePrivate * priv; /* @< Don't mess with this! */ +} gdispImage; + +#ifdef __cplusplus +extern "C" { +#endif + + /* + * Deprecated Functions. + */ + gdispImageError DEPRECATED("Use gdispImageOpenGFile() instead") gdispImageOpen(gdispImage *img); + bool_t DEPRECATED("Use gdispImageOpenMemory() instead") gdispImageSetMemoryReader(gdispImage *img, const void *memimage); + #if GFX_USE_OS_CHIBIOS + bool_t DEPRECATED("Use gdispImageOpenBaseFileStream() instead") gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr); + #endif + #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX + bool_t DEPRECATED("Please use gdispImageOpenFile() instead") gdispImageSetFileReader(gdispImage *img, const char *filename); + #define gdispImageSetSimulFileReader(img, fname) gdispImageSetFileReader(img, fname) + #endif + + /** + * @brief Initialise a gdispImage object + * + * @param[in] img The image structure to initialise + * + */ + void gdispImageInit(gdispImage *img); + + /** + * @brief Open an image using an open GFILE and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @param[in] img The image structure + * @param[in] f The open GFILE stream. + * + * @pre The GFILE must be open for reading. + * + * @note This determines which decoder to use and then initialises all other fields + * in the gdispImage structure. + * @note The image background color is set to White. + * @note There are three types of return - everything OK, partial success and unrecoverable + * failures. For everything OK it returns GDISP_IMAGE_ERR_OK. A partial success can + * be distinguished from a unrecoverable failure by testing the GDISP_IMAGE_ERR_UNRECOVERABLE + * bit in the error code. + * A partial success return code means an image can still be drawn but perhaps with + * reduced functionality eg only the first page of a multi-page image. + * @note @p gdispImageClose() should be called when finished with the image. This will close + * the image and its underlying GFILE file. Note that images opened with partial success + * (eg GDISP_IMAGE_ERR_UNSUPPORTED_OK) + * still need to be closed when you are finished with them. + */ + gdispImageError gdispImageOpenGFile(gdispImage *img, GFILE *f); + + /** + * @brief Open an image in a file and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @pre You must have included the file-system support into GFILE that you want to use. + * + * @param[in] img The image structure + * @param[in] filename The filename to open + * + * @note This function just opens the GFILE using the filename and passes it to @p gdispImageOpenGFile(). + */ + #define gdispImageOpenFile(img, filename) gdispImageOpenGFile((img), gfileOpen((filename), "rb")) + + /** + * @brief Open an image in a ChibiOS basefilestream and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @pre GFILE_NEED_CHIBIOSFS and GFX_USE_OS_CHIBIOS must be TRUE. This only makes sense on the ChibiOS + * operating system. + * + * @param[in] img The image structure + * @param[in] BaseFileStreamPtr A pointer to an open BaseFileStream + * + * @note This function just opens the GFILE using the basefilestream and passes it to @p gdispImageOpenGFile(). + */ + #define gdispImageOpenBaseFileStream(img, BaseFileStreamPtr) gdispImageOpenGFile((img), gfileOpenBaseFileStream((BaseFileStreamPtr), "rb")) + + /** + * @brief Open an image in memory and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @pre GFILE_NEED_MEMFS must be TRUE + * + * @param[in] img The image structure + * @param[in] ptr A pointer to the image bytes in memory + * + * @note This function just opens the GFILE using the basefilestream and passes it to @p gdispImageOpenGFile(). + */ + #define gdispImageOpenMemory(img, ptr) gdispImageOpenGFile((img), gfileOpenMemory((void *)(ptr), "rb")) + + /** + * @brief Close an image and release any dynamically allocated working storage. + * + * @param[in] img The image structure + * + * @pre gdispImageOpenFile() must have returned successfully. + * + * @note Also calls the IO close function (if it hasn't already been called). + */ + void gdispImageClose(gdispImage *img); + + /** + * @brief Is an image open. + * @return TRUE if the image is currently open. + * + * @param[in] img The image structure + * + * @note Be careful with calling this on an uninitialized image structure as the image + * will contain random data which may be interpreted as meaning the image + * is open. Clearing the Image structure to 0's will guarantee the image + * is seen as being closed. + */ + bool_t gdispImageIsOpen(gdispImage *img); + + /** + * @brief Set the background color of the image. + * + * @param[in] img The image structure + * @param[in] bgcolor The background color to use + * + * @pre gdispImageOpen() must have returned successfully. + * + * @note This color is only used when an image has to restore part of the background before + * continuing with drawing that includes transparency eg some GIF animations. + */ + void gdispImageSetBgColor(gdispImage *img, color_t bgcolor); + + /** + * @brief Cache the image + * @details Decodes and caches the current frame into RAM. + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @param[in] img The image structure + * + * @pre gdispImageOpen() must have returned successfully. + * + * @note This can use a LOT of RAM! + * @note The decoder may choose to ignore the request for caching. If it does so it will + * return GDISP_IMAGE_ERR_UNSUPPORTED_OK. + * @note A fatal error here does not necessarily mean that drawing the image will fail. For + * example, a GDISP_IMAGE_ERR_NOMEMORY error simply means there isn't enough RAM to + * cache the image. + */ + gdispImageError gdispImageCache(gdispImage *img); + + /** + * @brief Draw the image + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @param[in] g The display to draw on + * @param[in] img The image structure + * @param[in] x,y The screen 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) + */ + gdispImageError gdispGImageDraw(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + #define gdispImageDraw(img,x,y,cx,cy,sx,sy) gdispGImageDraw(GDISP,img,x,y,cx,cy,sx,sy) + + /** + * @brief Prepare for the next frame/page in the image file. + * @return A time in milliseconds to keep displaying the current frame before trying to draw + * the next frame. Watch out for the special values TIME_IMMEDIATE and TIME_INFINITE. + * + * @param[in] img The image structure + * + * @pre gdispImageOpen() must have returned successfully. + * + * @note It will return TIME_IMMEDIATE if the first frame/page hasn't been drawn or if the next frame + * should be drawn immediately. + * @note It will return TIME_INFINITE if another image frame doesn't exist or an error has occurred. + * @note Images that support multiple pages (eg TIFF files) will return TIME_IMMEDIATE between pages + * and then TIME_INFINITE when there are no more pages. + * @note An image that displays a looped animation will never return TIME_INFINITE unless it + * gets an error. + * @note Calling gdispImageDraw() after getting a TIME_INFINITE will go back to drawing the first + * frame/page. + */ + delaytime_t gdispImageNext(gdispImage *img); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ +#endif /* _GDISP_IMAGE_H */ +/** @} */ + diff --git a/src/gdisp/gdisp_image_bmp.c b/src/gdisp/gdisp_image_bmp.c new file mode 100644 index 00000000..8ff40ca0 --- /dev/null +++ b/src/gdisp/gdisp_image_bmp.c @@ -0,0 +1,904 @@ +/* + * 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/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 + */ +void *gdispImageAlloc(gdispImage *img, size_t sz); +void gdispImageFree(gdispImage *img, void *ptr, size_t sz); + +/** + * How big a pixel array to allocate for blitting (in pixels) + * Bigger is faster but uses more RAM. + * This must be greater than 40 bytes and 32 pixels as we read our headers into this space as well + */ +#define BLIT_BUFFER_SIZE 32 + +/* + * Determining endianness as at compile time is not guaranteed or compiler portable. + * We use the best test we can. If we can't guarantee little endianness we do things the + * hard way. + */ +#define GUARANTEED_LITTLE_ENDIAN (!defined(SAFE_ENDIAN) && !defined(SAFE_ALIGNMENT) && (\ + (defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \ + || defined(__LITTLE_ENDIAN__) \ + || defined(__LITTLE_ENDIAN) \ + || defined(_LITTLE_ENDIAN) \ +/* || (1 == *(unsigned char *)&(const int){1})*/ \ + )) + + +/* This is a runtime test */ +static const uint8_t dwordOrder[4] = { 1, 2, 3, 4 }; + +#define isWordLittleEndian() (*(uint16_t *)&dwordOrder == 0x0201) +#define isDWordLittleEndian() (*(uint32_t *)&dwordOrder == 0x04030201) + +#if GUARANTEED_LITTLE_ENDIAN + /* These are fast routines for guaranteed little endian machines */ + #define CONVERT_FROM_WORD_LE(w) + #define CONVERT_FROM_DWORD_LE(dw) +#else + /* These are slower routines for when little endianness cannot be guaranteed at compile time */ + #define CONVERT_FROM_WORD_LE(w) { if (!isWordLittleEndian()) w = ((((uint16_t)(w))>>8)|(((uint16_t)(w))<<8)); } + #define CONVERT_FROM_DWORD_LE(dw) { if (!isDWordLittleEndian()) dw = (((uint32_t)(((const uint8_t *)(&dw))[0]))|(((uint32_t)(((const uint8_t *)(&dw))[1]))<<8)|(((uint32_t)(((const uint8_t *)(&dw))[2]))<<16)|(((uint32_t)(((const uint8_t *)(&dw))[3]))<<24)); } +#endif + +typedef struct gdispImagePrivate { + uint8_t bmpflags; + #define BMP_V2 0x01 // Version 2 (old) header format + #define BMP_V4 0x02 // Version 4 (alpha support) header format + #define BMP_PALETTE 0x04 // Uses a palette + #define BMP_COMP_RLE 0x08 // Uses RLE compression + #define BMP_COMP_MASK 0x10 // Uses mask & shift decoding + #define BMP_RLE_ENC 0x20 // Currently in RLE encoded run + #define BMP_RLE_ABS 0x40 // Currently in RLE absolute run + #define BMP_TOP_TO_BOTTOM 0x80 // Decodes bottom to top line + uint8_t bitsperpixel; +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + uint16_t palsize; + pixel_t *palette; +#endif +#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE + uint16_t rlerun; + uint8_t rlecode; +#endif +#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 + int8_t shiftred; + int8_t shiftgreen; + int8_t shiftblue; + int8_t shiftalpha; + uint32_t maskred; + uint32_t maskgreen; + uint32_t maskblue; + uint32_t maskalpha; +#endif + size_t frame0pos; + pixel_t *frame0cache; + pixel_t buf[BLIT_BUFFER_SIZE]; + } gdispImagePrivate; + +void gdispImageClose_BMP(gdispImage *img) { + if (img->priv) { +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + if (img->priv->palette) + gdispImageFree(img, (void *)img->priv->palette, img->priv->palsize*sizeof(color_t)); +#endif + if (img->priv->frame0cache) + gdispImageFree(img, (void *)img->priv->frame0cache, img->width*img->height*sizeof(pixel_t)); + gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); + img->priv = 0; + } +} + +gdispImageError gdispImageOpen_BMP(gdispImage *img) { + gdispImagePrivate *priv; + uint8_t hdr[2]; + uint16_t aword; + uint32_t adword; + uint32_t offsetColorTable; + + /* Read the file identifier */ + if (gfileRead(img->f, hdr, 2) != 2) + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + /* Process the BITMAPFILEHEADER structure */ + + /** + * We only accept Windows V2+ bitmaps. + * - we don't support OS/2 bitmaps, icons, pointers, or Windows V1 bitmaps. + */ + if (hdr[0] != 'B' || hdr[1] != 'M') + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + /* We know we are a BMP format image */ + img->flags = 0; + + /* Allocate our private area */ + if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) + return GDISP_IMAGE_ERR_NOMEMORY; + + /* Initialise the essential bits in the private area */ + priv = img->priv; + priv->frame0cache = 0; + priv->bmpflags = 0; +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + priv->palette = 0; +#endif + + /* Skip the size field and the 2 reserved fields */ + if (gfileRead(img->f, priv->buf, 8) != 8) + goto baddatacleanup; + + /* Get the offset to the bitmap data */ + if (gfileRead(img->f, &priv->frame0pos, 4) != 4) + goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->frame0pos); + + /* Process the BITMAPCOREHEADER structure */ + + /* Get the offset to the colour data */ + if (gfileRead(img->f, &offsetColorTable, 4) != 4) + goto baddatacleanup; + CONVERT_FROM_DWORD_LE(offsetColorTable); + offsetColorTable += 14; // Add the size of the BITMAPFILEHEADER + + // Detect our bitmap version + if (offsetColorTable == 12+14) { + img->priv->bmpflags |= BMP_V2; + + // Read the header + if (gfileRead(img->f, priv->buf, 12-4) != 12-4) + goto baddatacleanup; + // Get the width + img->width = *(uint16_t *)(((uint8_t *)priv->buf)+0); + CONVERT_FROM_WORD_LE(img->width); + // Get the height + img->height = *(uint16_t *)(((uint8_t *)priv->buf)+2); + CONVERT_FROM_WORD_LE(img->height); + if (img->height < 0) { + img->priv->bmpflags |= BMP_TOP_TO_BOTTOM; + img->height = -img->height; + } + // Get the planes + aword = *(uint16_t *)(((uint8_t *)priv->buf)+4); + CONVERT_FROM_WORD_LE(aword); + if (aword != 1) + goto unsupportedcleanup; + // Get the bits per pixel + aword = *(uint16_t *)(((uint8_t *)priv->buf)+6); + CONVERT_FROM_WORD_LE(aword); + switch(aword) { +#if GDISP_NEED_IMAGE_BMP_1 + case 1: +#endif +#if GDISP_NEED_IMAGE_BMP_4 + case 4: +#endif +#if GDISP_NEED_IMAGE_BMP_8 + case 8: +#endif +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_8 + priv->bmpflags |= BMP_PALETTE; + priv->palsize = 1<bitsperpixel = aword; + + } else if (offsetColorTable >= 40+14) { + if (offsetColorTable > 40+14) + priv->bmpflags |= BMP_V4; + + // Read the header + if (gfileRead(img->f, priv->buf, 40-4) != 40-4) + goto baddatacleanup; + // Get the width + adword = *(uint32_t *)(((uint8_t *)priv->buf)+0); + CONVERT_FROM_DWORD_LE(adword); + if (adword > 32768) // This also picks up negative values + goto unsupportedcleanup; + img->width = adword; + // Get the height + adword = *(uint32_t *)(((uint8_t *)priv->buf)+4); + CONVERT_FROM_DWORD_LE(adword); + if ((int32_t)adword < 0) { // Negative test + priv->bmpflags |= BMP_TOP_TO_BOTTOM; + adword = -adword; + } + if (adword > 32768) + goto unsupportedcleanup; + img->height = adword; + // Get the planes + aword = *(uint16_t *)(((uint8_t *)priv->buf)+8); + CONVERT_FROM_WORD_LE(aword); + if (aword != 1) + goto unsupportedcleanup; + // Get the bits per pixel + aword = *(uint16_t *)(((uint8_t *)priv->buf)+10); + CONVERT_FROM_WORD_LE(aword); + switch(aword) { +#if GDISP_NEED_IMAGE_BMP_1 + case 1: +#endif +#if GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE + case 4: +#endif +#if GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + case 8: +#endif +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + priv->bmpflags |= BMP_PALETTE; + priv->palsize = 1<bitsperpixel = aword; + // Get the compression + adword = *(uint32_t *)(((uint8_t *)priv->buf)+12); + CONVERT_FROM_DWORD_LE(adword); + switch(adword) { + case 0: // BI_RGB - uncompressed + break; +#if GDISP_NEED_IMAGE_BMP_8_RLE + case 1: // BI_RLE8 compression + if (priv->bitsperpixel != 8) + goto unsupportedcleanup; + priv->bmpflags |= BMP_COMP_RLE; + break; +#endif +#if GDISP_NEED_IMAGE_BMP_4_RLE + case 2: // BI_RLE4 compression + if (priv->bitsperpixel != 4) + goto unsupportedcleanup; + priv->bmpflags |= BMP_COMP_RLE; + break; +#endif +#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 + case 3: // BI_BITFIELDS decoding + if (priv->bitsperpixel < 16 || priv->bitsperpixel == 24) + goto unsupportedcleanup; + priv->bmpflags |= BMP_COMP_MASK; + if (priv->bmpflags & BMP_V4) // V4 stored the masks in the header + offsetColorTable = 40+14; + break; +#endif + default: + goto unsupportedcleanup; + } + priv->bitsperpixel = aword; +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + // Get the actual colors used + adword = *(uint32_t *)(((uint8_t *)priv->buf)+28); + CONVERT_FROM_DWORD_LE(adword); + if (adword && adword < priv->palsize) + priv->palsize = adword; +#endif + } else + goto baddatacleanup; + +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + /* Load the palette tables */ + if (priv->bmpflags & BMP_PALETTE) { + gfileSetPos(img->f, offsetColorTable); + + if (!(priv->palette = (color_t *)gdispImageAlloc(img, priv->palsize*sizeof(color_t)))) + return GDISP_IMAGE_ERR_NOMEMORY; + if (priv->bmpflags & BMP_V2) { + for(aword = 0; aword < priv->palsize; aword++) { + if (gfileRead(img->f, &priv->buf, 3) != 3) goto baddatacleanup; + priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[2], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[0]); + } + } else { + for(aword = 0; aword < priv->palsize; aword++) { + if (gfileRead(img->f, &priv->buf, 4) != 4) goto baddatacleanup; + priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[2], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[0]); + } + } + + } +#endif + +#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 + /* Load the bit masks */ + if (priv->bmpflags & BMP_COMP_MASK) { + gfileSetPos(img->f, offsetColorTable); + if (gfileRead(img->f, &priv->maskred, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskred); + if (gfileRead(img->f, &priv->maskgreen, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskgreen); + if (gfileRead(img->f, &priv->maskblue, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskblue); + if (priv->bmpflags & BMP_V4) { + if (gfileRead(img->f, &priv->maskalpha, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskalpha); + } else + priv->maskalpha = 0; + } else if (priv->bitsperpixel == 16) { + priv->bmpflags |= BMP_COMP_MASK; + priv->maskred = 0x7C00; + priv->maskgreen = 0x03E0; + priv->maskblue = 0x001F; + priv->maskalpha = 0; + } else if (priv->bitsperpixel == 32) { + priv->bmpflags |= BMP_COMP_MASK; + priv->maskred = 0x00FF0000; + priv->maskgreen = 0x0000FF00; + priv->maskblue = 0x000000FF; + priv->maskalpha = 0; + } + + /* We need to adjust the masks and calculate the shift values so the result scales 0 -> 255 */ + if (priv->bmpflags & BMP_COMP_MASK) { + priv->shiftred = 0; + priv->shiftgreen = 0; + priv->shiftblue = 0; + if (priv->maskred) { + if (priv->maskred < 256) + for(adword = priv->maskred; adword < 128; priv->shiftred--, adword <<= 1); + else + for(adword = priv->maskred; adword > 255; priv->shiftred++, adword >>= 1); + } + if (priv->maskgreen) { + if (priv->maskgreen < 256) + for(adword = priv->maskgreen; adword < 128; priv->shiftgreen--, adword <<= 1); + else + for(adword = priv->maskgreen; adword > 255; priv->shiftgreen++, adword >>= 1); + } + if (priv->maskblue) { + if (priv->maskblue < 256) + for(adword = priv->maskblue; adword < 128; priv->shiftblue--, adword <<= 1); + else + for(adword = priv->maskblue; adword > 255; priv->shiftblue++, adword >>= 1); + } + if (priv->maskalpha) { + if (priv->maskalpha < 256) + for(adword = priv->maskalpha; adword < 128; priv->shiftalpha--, adword <<= 1); + else + for(adword = priv->maskalpha; adword > 255; priv->shiftalpha++, adword >>= 1); + } + } +#endif + + img->type = GDISP_IMAGE_TYPE_BMP; + return GDISP_IMAGE_ERR_OK; + +baddatacleanup: + gdispImageClose_BMP(img); // Clean up the private data area + return GDISP_IMAGE_ERR_BADDATA; // Oops - something wrong + +unsupportedcleanup: + gdispImageClose_BMP(img); // Clean up the private data area + return GDISP_IMAGE_ERR_UNSUPPORTED; // Not supported +} + +static coord_t getPixels(gdispImage *img, coord_t x) { + gdispImagePrivate * priv; + color_t * pc; + coord_t len; + + priv = img->priv; + pc = priv->buf; + len = 0; + + switch(priv->bitsperpixel) { +#if GDISP_NEED_IMAGE_BMP_1 + case 1: + { + uint8_t b[4]; + uint8_t m; + + priv = img->priv; + pc = priv->buf; + len = 0; + + while(x < img->width && len <= BLIT_BUFFER_SIZE-32) { + if (gfileRead(img->f, &b, 4) != 4) + return 0; + + for(m=0x80; m; m >>= 1, pc++) + pc[0] = priv->palette[(m&b[0]) ? 1 : 0]; + for(m=0x80; m; m >>= 1, pc++) + pc[0] = priv->palette[(m&b[1]) ? 1 : 0]; + for(m=0x80; m; m >>= 1, pc++) + pc[0] = priv->palette[(m&b[2]) ? 1 : 0]; + for(m=0x80; m; m >>= 1, pc++) + pc[0] = priv->palette[(m&b[3]) ? 1 : 0]; + len += 32; + x += 32; + } + } + return len; +#endif + +#if GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE + case 4: + #if GDISP_NEED_IMAGE_BMP_4_RLE + #if GDISP_NEED_IMAGE_BMP_4 + if (priv->bmpflags & BMP_COMP_RLE) + #endif + { + uint8_t b[4]; + + while(x < img->width) { + if (priv->bmpflags & BMP_RLE_ENC) { + while (priv->rlerun && len <= BLIT_BUFFER_SIZE-2 && x < img->width) { + *pc++ = priv->palette[priv->rlecode >> 4]; + priv->rlerun--; + len++; + x++; + if (priv->rlerun) { + *pc++ = priv->palette[priv->rlecode & 0x0F]; + priv->rlerun--; + len++; + x++; + } + } + if (priv->rlerun) // Return if we have more run to do + return len; + } else if (priv->bmpflags & BMP_RLE_ABS) { + while (priv->rlerun && len <= BLIT_BUFFER_SIZE-2 && x < img->width) { + if (gfileRead(img->f, &b, 1) != 1) + return 0; + *pc++ = priv->palette[b[0] >> 4]; + priv->rlerun--; + len++; + x++; + if (priv->rlerun) { + *pc++ = priv->palette[b[0] & 0x0F]; + priv->rlerun--; + len++; + x++; + } + } + if (priv->rlerun) // Return if we have more run to do + return len; + if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary + if (gfileRead(img->f, &b, 1) != 1) + return 0; + } + } + + // We have finished the current run - read a new run + priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS); + + // There are always at least 2 bytes in an RLE code + if (gfileRead(img->f, &b, 2) != 2) + return 0; + + if (b[0]) { // Encoded mode + priv->rlerun = b[0]; + priv->rlecode = b[1]; + priv->bmpflags |= BMP_RLE_ENC; + } else if (b[1] == 0) { // End of line + if (x < img->width) { + priv->rlerun = img->width - x; + priv->rlecode = 0; // Who knows what color this should really be + priv->bmpflags |= BMP_RLE_ENC; + } + } else if (b[1] == 1) { // End of file + return len; + } else if (b[1] == 2) { // Delta x, y + // There are always at least 2 bytes in an RLE code + if (gfileRead(img->f, &b, 2) != 2) + return 0; + priv->rlerun = b[0] + (uint16_t)b[1] * img->width; + priv->rlecode = 0; // Who knows what color this should really be + priv->bmpflags |= BMP_RLE_ENC; + } else { // Absolute mode + priv->rlerun = b[1]; + priv->bmpflags |= BMP_RLE_ABS; + } + } + return len; + } + #endif + #if GDISP_NEED_IMAGE_BMP_4 + { + uint8_t b[4]; + + while(x < img->width && len <= BLIT_BUFFER_SIZE-8) { + if (gfileRead(img->f, &b, 4) != 4) + return 0; + + *pc++ = priv->palette[b[0] >> 4]; + *pc++ = priv->palette[b[0] & 0x0F]; + *pc++ = priv->palette[b[1] >> 4]; + *pc++ = priv->palette[b[1] & 0x0F]; + *pc++ = priv->palette[b[2] >> 4]; + *pc++ = priv->palette[b[2] & 0x0F]; + *pc++ = priv->palette[b[3] >> 4]; + *pc++ = priv->palette[b[3] & 0x0F]; + len += 8; + x += 8; + } + return len; + } + #endif +#endif + +#if GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + case 8: + #if GDISP_NEED_IMAGE_BMP_8_RLE + #if GDISP_NEED_IMAGE_BMP_8 + if (priv->bmpflags & BMP_COMP_RLE) + #endif + { + uint8_t b[4]; + + while(x < img->width) { + if (priv->bmpflags & BMP_RLE_ENC) { + while (priv->rlerun && len < BLIT_BUFFER_SIZE && x < img->width) { + *pc++ = priv->palette[priv->rlecode]; + priv->rlerun--; + len++; + x++; + } + if (priv->rlerun) // Return if we have more run to do + return len; + } else if (priv->bmpflags & BMP_RLE_ABS) { + while (priv->rlerun && len < BLIT_BUFFER_SIZE && x < img->width) { + if (gfileRead(img->f, &b, 1) != 1) + return 0; + *pc++ = priv->palette[b[0]]; + priv->rlerun--; + len++; + x++; + } + if (priv->rlerun) // Return if we have more run to do + return len; + if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary + if (gfileRead(img->f, &b, 1) != 1) + return 0; + } + } + + // We have finished the current run - read a new run + priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS); + + // There are always at least 2 bytes in an RLE code + if (gfileRead(img->f, &b, 2) != 2) + return 0; + + if (b[0]) { // Encoded mode + priv->rlerun = b[0]; + priv->rlecode = b[1]; + priv->bmpflags |= BMP_RLE_ENC; + } else if (b[1] == 0) { // End of line + if (x < img->width) { + priv->rlerun = img->width - x; + priv->rlecode = 0; // Who knows what color this should really be + priv->bmpflags |= BMP_RLE_ENC; + } + } else if (b[1] == 1) { // End of file + return len; + } else if (b[1] == 2) { // Delta x, y + // There are always at least 2 bytes in an RLE code + if (gfileRead(img->f, &b, 2) != 2) + return GDISP_IMAGE_ERR_BADDATA; + priv->rlerun = b[0] + (uint16_t)b[1] * img->width; + priv->rlecode = 0; // Who knows what color this should really be + priv->bmpflags |= BMP_RLE_ENC; + } else { // Absolute mode + priv->rlerun = b[1]; + priv->bmpflags |= BMP_RLE_ABS; + } + } + return len; + } + #endif + #if GDISP_NEED_IMAGE_BMP_8 + { + uint8_t b[4]; + + while(x < img->width && len <= BLIT_BUFFER_SIZE-4) { + if (gfileRead(img->f, &b, 4) != 4) + return 0; + + *pc++ = priv->palette[b[0]]; + *pc++ = priv->palette[b[1]]; + *pc++ = priv->palette[b[2]]; + *pc++ = priv->palette[b[3]]; + len += 4; + x += 4; + } + return len; + } + #endif +#endif + +#if GDISP_NEED_IMAGE_BMP_16 + case 16: + { + uint16_t w[2]; + color_t r, g, b; + + while(x < img->width && len <= BLIT_BUFFER_SIZE-2) { + if (gfileRead(img->f, &w, 4) != 4) + return 0; + CONVERT_FROM_WORD_LE(w[0]); + CONVERT_FROM_WORD_LE(w[1]); + if (priv->shiftred < 0) + r = (color_t)((w[0] & priv->maskred) << -priv->shiftred); + else + r = (color_t)((w[0] & priv->maskred) >> priv->shiftred); + if (priv->shiftgreen < 0) + g = (color_t)((w[0] & priv->maskgreen) << -priv->shiftgreen); + else + g = (color_t)((w[0] & priv->maskgreen) >> priv->shiftgreen); + if (priv->shiftblue < 0) + b = (color_t)((w[0] & priv->maskblue) << -priv->shiftblue); + else + b = (color_t)((w[0] & priv->maskblue) >> priv->shiftblue); + /* We don't support alpha yet */ + *pc++ = RGB2COLOR(r, g, b); + if (priv->shiftred < 0) + r = (color_t)((w[1] & priv->maskred) << -priv->shiftred); + else + r = (color_t)((w[1] & priv->maskred) >> priv->shiftred); + if (priv->shiftgreen < 0) + g = (color_t)((w[1] & priv->maskgreen) << -priv->shiftgreen); + else + g = (color_t)((w[1] & priv->maskgreen) >> priv->shiftgreen); + if (priv->shiftblue < 0) + b = (color_t)((w[1] & priv->maskblue) << -priv->shiftblue); + else + b = (uint8_t)((w[1] & priv->maskblue) >> priv->shiftblue); + /* We don't support alpha yet */ + *pc++ = RGB2COLOR(r, g, b); + x += 2; + len += 2; + } + } + return len; +#endif + +#if GDISP_NEED_IMAGE_BMP_24 + case 24: + { + uint8_t b[3]; + + while(x < img->width && len < BLIT_BUFFER_SIZE) { + if (gfileRead(img->f, &b, 3) != 3) + return 0; + *pc++ = RGB2COLOR(b[2], b[1], b[0]); + x++; + len++; + } + + if (x >= img->width) { + // Make sure we have read a multiple of 4 bytes for the line + if ((x & 3) && gfileRead(img->f, &b, x & 3) != (x & 3)) + return 0; + } + } + return len; +#endif + +#if GDISP_NEED_IMAGE_BMP_32 + case 32: + { + uint32_t dw; + color_t r, g, b; + + while(x < img->width && len < BLIT_BUFFER_SIZE) { + if (gfileRead(img->f, &dw, 4) != 4) + return 0; + CONVERT_FROM_DWORD_LE(dw); + if (priv->shiftred < 0) + r = (color_t)((dw & priv->maskred) << -priv->shiftred); + else + r = (color_t)((dw & priv->maskred) >> priv->shiftred); + if (priv->shiftgreen < 0) + g = (color_t)((dw & priv->maskgreen) << -priv->shiftgreen); + else + g = (color_t)((dw & priv->maskgreen) >> priv->shiftgreen); + if (priv->shiftblue < 0) + b = (color_t)((dw & priv->maskblue) << -priv->shiftblue); + else + b = (color_t)((dw & priv->maskblue) >> priv->shiftblue); + /* We don't support alpha yet */ + *pc++ = RGB2COLOR(r, g, b); + x++; + len++; + } + } + return len; +#endif + + default: + return len; + } +} + +gdispImageError gdispImageCache_BMP(gdispImage *img) { + gdispImagePrivate * priv; + color_t * pcs; + color_t * pcd; + coord_t pos, x, y; + size_t len; + + /* If we are already cached - just return OK */ + priv = img->priv; + if (priv->frame0cache) + return GDISP_IMAGE_ERR_OK; + + /* We need to allocate the cache */ + len = img->width * img->height * sizeof(pixel_t); + priv->frame0cache = (pixel_t *)gdispImageAlloc(img, len); + if (!priv->frame0cache) + return GDISP_IMAGE_ERR_NOMEMORY; + + /* Read the entire bitmap into cache */ + gfileSetPos(img->f, priv->frame0pos); +#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE + priv->rlerun = 0; + priv->rlecode = 0; +#endif + + if (priv->bmpflags & BMP_TOP_TO_BOTTOM) { + for(y = 0, pcd = priv->frame0cache; y < img->height; y++) { + x = 0; pos = 0; + while(x < img->width) { + if (!pos) { + if (!(pos = getPixels(img, x))) + return GDISP_IMAGE_ERR_BADDATA; + pcs = priv->buf; + } + *pcd++ = *pcs++; + x++; pos--; + } + } + } else { + for(y = img->height-1, pcd = priv->frame0cache + img->width*(img->height-1); y >= 0; y--, pcd -= 2*img->width) { + x = 0; pos = 0; + while(x < img->width) { + if (!pos) { + if (!(pos = getPixels(img, x))) + return GDISP_IMAGE_ERR_BADDATA; + pcs = priv->buf; + } + *pcd++ = *pcs++; + x++; pos--; + } + } + } + + return GDISP_IMAGE_ERR_OK; +} + +gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { + gdispImagePrivate * priv; + coord_t mx, my; + coord_t pos, len, st; + + priv = img->priv; + + /* Check some reasonableness */ + if (sx >= img->width || sy >= img->height) return GDISP_IMAGE_ERR_OK; + if (sx + cx > img->width) cx = img->width - sx; + if (sy + cy > img->height) cy = img->height - sy; + + /* Draw from the image cache - if it exists */ + if (priv->frame0cache) { + gdispGBlitArea(g, x, y, cx, cy, sx, sy, img->width, priv->frame0cache); + return GDISP_IMAGE_ERR_OK; + } + + /* Start decoding from the beginning */ + gfileSetPos(img->f, priv->frame0pos); +#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE + priv->rlerun = 0; + priv->rlecode = 0; +#endif + + if (priv->bmpflags & BMP_TOP_TO_BOTTOM) { + for(my = 0; my < img->height; my++) { + mx = 0; + while(mx < img->width) { + if (!(pos = getPixels(img, mx))) + return GDISP_IMAGE_ERR_BADDATA; + if (my >= sy && my < sy+cy && mx < sx+cx && mx+pos >= sx) { + st = mx < sx ? sx - mx : 0; + len = pos-st; + if (mx+st+len > sx+cx) len = sx+cx-mx-st; + if (len == 1) + gdispGDrawPixel(g, x+mx+st-sx, y+my-sy, priv->buf[st]); + else + gdispGBlitArea(g, x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf); + } + mx += pos; + } + } + } else { + for(my = img->height-1; my >= 0; my--) { + mx = 0; + while(mx < img->width) { + if (!(pos = getPixels(img, mx))) + return GDISP_IMAGE_ERR_BADDATA; + if (my >= sy && my < sy+cy && mx < sx+cx && mx+pos >= sx) { + st = mx < sx ? sx - mx : 0; + len = pos-st; + if (mx+st+len > sx+cx) len = sx+cx-mx-st; + if (len == 1) + gdispGDrawPixel(g, x+mx+st-sx, y+my-sy, priv->buf[st]); + else + gdispGBlitArea(g, x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf); + } + mx += pos; + } + } + } + + return GDISP_IMAGE_ERR_OK; +} + +delaytime_t gdispImageNext_BMP(gdispImage *img) { + (void) img; + + /* No more frames/pages */ + return TIME_INFINITE; +} + +#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 new file mode 100644 index 00000000..06f4ef6a --- /dev/null +++ b/src/gdisp/gdisp_image_gif.c @@ -0,0 +1,1208 @@ +/* + * 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/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 + +/** + * Helper Routines Needed + */ +void *gdispImageAlloc(gdispImage *img, size_t sz); +void gdispImageFree(gdispImage *img, void *ptr, size_t sz); + +/** + * How big an array to allocate for blitting (in pixels) + * Bigger is faster but uses more RAM. + */ +#define BLIT_BUFFER_SIZE 32 + +/* + * Determining endianness as at compile time is not guaranteed or compiler portable. + * We use the best test we can. If we can't guarantee little endianness we do things the + * hard way. + */ +#define GUARANTEED_LITTLE_ENDIAN (!defined(SAFE_ENDIAN) && !defined(SAFE_ALIGNMENT) && (\ + (defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \ + || defined(__LITTLE_ENDIAN__) \ + || defined(__LITTLE_ENDIAN) \ + || defined(_LITTLE_ENDIAN) \ +/* || (1 == *(unsigned char *)&(const int){1})*/ \ + )) + + +/* This is a runtime test */ +static const uint8_t dwordOrder[4] = { 1, 2, 3, 4 }; + +#define isWordLittleEndian() (*(uint16_t *)&dwordOrder == 0x0201) +#define isDWordLittleEndian() (*(uint32_t *)&dwordOrder == 0x04030201) + +#if GUARANTEED_LITTLE_ENDIAN + /* These are fast routines for guaranteed little endian machines */ + #define CONVERT_FROM_WORD_LE(w) + #define CONVERT_FROM_DWORD_LE(dw) +#else + /* These are slower routines for when little endianness cannot be guaranteed at compile time */ + #define CONVERT_FROM_WORD_LE(w) { if (!isWordLittleEndian()) w = ((((uint16_t)(w))>>8)|(((uint16_t)(w))<<8)); } + #define CONVERT_FROM_DWORD_LE(dw) { if (!isDWordLittleEndian()) dw = (((uint32_t)(((const uint8_t *)(&dw))[0]))|(((uint32_t)(((const uint8_t *)(&dw))[1]))<<8)|(((uint32_t)(((const uint8_t *)(&dw))[2]))<<16)|(((uint32_t)(((const uint8_t *)(&dw))[3]))<<24)); } +#endif + +// We need a special error to indicate the end of file (which may not actually be an error) +#define GDISP_IMAGE_EOF ((gdispImageError)-1) +#define GDISP_IMAGE_LOOP ((gdispImageError)-2) + +#define MAX_CODE_BITS 12 +#define CODE_MAX ((1<priv; + + // We need the decode ram, and possibly a palette + if (!(decode = (imgdecode *)gdispImageAlloc(img, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)))) + return GDISP_IMAGE_ERR_NOMEMORY; + + // We currently have not read any image data block + decode->blocksz = 0; + + // Set the palette + if (priv->frame.palsize) { + // Local palette + decode->maxpixel = priv->frame.palsize-1; + decode->palette = (color_t *)(decode+1); + gfileSetPos(img->f, priv->frame.pospal); + for(cnt = 0; cnt < priv->frame.palsize; cnt++) { + if (gfileRead(img->f, &decode->buf, 3) != 3) + goto baddatacleanup; + decode->palette[cnt] = RGB2COLOR(decode->buf[0], decode->buf[1], decode->buf[2]); + } + } else if (priv->palette) { + // Global palette + decode->maxpixel = priv->palsize-1; + decode->palette = priv->palette; + } else { + // Oops - we must have a palette + goto baddatacleanup; + } + + // Get the initial lzw code size and values + gfileSetPos(img->f, priv->frame.posimg); + if (gfileRead(img->f, &decode->bitsperpixel, 1) != 1 || decode->bitsperpixel >= MAX_CODE_BITS) + goto baddatacleanup; + decode->code_clear = 1 << decode->bitsperpixel; + decode->code_eof = decode->code_clear + 1; + decode->code_max = decode->code_clear + 2; + decode->code_last = CODE_NONE; + decode->bitspercode = decode->bitsperpixel+1; + decode->maxcodesz = 1 << decode->bitspercode; + decode->shiftbits = 0; + decode->shiftdata = 0; + decode->stackcnt = 0; + for(cnt = 0; cnt <= CODE_MAX; cnt++) + decode->prefix[cnt] = CODE_NONE; + + // All ready to go + priv->decode = decode; + return GDISP_IMAGE_ERR_OK; + +baddatacleanup: + gdispImageFree(img, decode, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)); + return GDISP_IMAGE_ERR_BADDATA; +} + +/** + * Stop decoding a frame. + * + * Pre: Frame info has been read. + */ +static void stopDecode(gdispImage *img) { + gdispImagePrivate * priv; + + priv = img->priv; + + // Free the decode data + if (priv->decode) { + gdispImageFree(img, (void *)priv->decode, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)); + priv->decode = 0; + } +} + +static uint16_t getPrefix(imgdecode *decode, uint16_t code) { + uint16_t i; + + for(i=0; code > decode->code_clear && i <= CODE_MAX; i++, code = decode->prefix[code]) { + if (code > CODE_MAX) + return CODE_NONE; + } + return code; +} + +/** + * Decode some pixels from a frame. + * + * Pre: We are ready for decoding. + * + * Return: The number of pixels decoded 0 .. BLIT_BUFFER_SIZE-1. 0 means EOF + * + * Note: The resulting pixels are stored in decode->buf + */ +static uint16_t getbytes(gdispImage *img) { + gdispImagePrivate * priv; + imgdecode * decode; + uint16_t cnt; + uint16_t code, prefix; + uint8_t bdata; + + priv = img->priv; + decode = priv->decode; + cnt = 0; + + // At EOF + if (decode->code_last == decode->code_eof) + return 0; + + while(cnt < sizeof(decode->buf)) { + // Use the stack up first + if (decode->stackcnt > 0) { + decode->buf[cnt++] = decode->stack[--decode->stackcnt]; + continue; + } + + // Get another code - a code is made up of decode->bitspercode bits. + while (decode->shiftbits < decode->bitspercode) { + // Get a byte - we may have to start a new data block + if ((!decode->blocksz && (gfileRead(img->f, &decode->blocksz, 1) != 1 || !decode->blocksz)) + || gfileRead(img->f, &bdata, 1) != 1) { + // Pretend we got the EOF code - some encoders seem to just end the file + decode->code_last = decode->code_eof; + return cnt; + } + decode->blocksz--; + + decode->shiftdata |= ((unsigned long)bdata) << decode->shiftbits; + decode->shiftbits += 8; + } + code = decode->shiftdata & BitMask[decode->bitspercode]; + decode->shiftdata >>= decode->bitspercode; + decode->shiftbits -= decode->bitspercode; + /** + * If code cannot fit into bitspercode bits we must raise its size. + * Note that codes above CODE_MAX are used for special signaling. + * If we're using MAX_CODE_BITS bits already and we're at the max code, just + * keep using the table as it is, don't increment decode->bitspercode. + */ + if (decode->code_max < CODE_MAX + 2 && ++decode->code_max > decode->maxcodesz && decode->bitspercode < MAX_CODE_BITS) { + decode->maxcodesz <<= 1; + decode->bitspercode++; + } + + // EOF - the appropriate way to stop decoding + if (code == decode->code_eof) { + // Skip to the end of the data blocks + do { + gfileSetPos(img->f, gfileGetPos(img->f)+decode->blocksz); + } while (gfileRead(img->f, &decode->blocksz, 1) == 1 && decode->blocksz); + + // Mark the end + decode->code_last = decode->code_eof; + break; + } + + if (code == decode->code_clear) { + // Start again + for(prefix = 0; prefix <= CODE_MAX; prefix++) + decode->prefix[prefix] = CODE_NONE; + decode->code_max = decode->code_eof + 1; + decode->bitspercode = decode->bitsperpixel + 1; + decode->maxcodesz = 1 << decode->bitspercode; + decode->code_last = CODE_NONE; + continue; + } + + if (code < decode->code_clear) { + // Simple unencoded pixel - add it + decode->buf[cnt++] = code; + + } else { + /** + * Its a LZW code - trace the linked list until the prefix is a + * valid pixel while pushing the suffix pixels on the stack. + * If done, pop the stack in reverse order adding the pixels + */ + if (decode->prefix[code] != CODE_NONE) + prefix = code; + + /** + * Only allowed if the code equals the partial code. + * In that case code = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! + */ + else if (code == decode->code_max - 2 && decode->stackcnt < sizeof(decode->stack)) { + prefix = decode->code_last; + decode->suffix[decode->code_max - 2] = decode->stack[decode->stackcnt++] = getPrefix(decode, decode->code_last); + } else + return 0; + + /** + * If the image is OK we should not get a CODE_NONE while tracing. + * To prevent looping with a bad image we use StackPtr as loop counter + * and stop before overflowing Stack[]. + */ + while (decode->stackcnt < sizeof(decode->stack) && prefix > decode->code_clear && prefix <= CODE_MAX) { + decode->stack[decode->stackcnt++] = decode->suffix[prefix]; + prefix = decode->prefix[prefix]; + } + if (decode->stackcnt >= sizeof(decode->stack) || prefix > CODE_MAX) + return 0; + + /* Push the last character on stack: */ + decode->stack[decode->stackcnt++] = prefix; + } + + if (decode->code_last != CODE_NONE && decode->prefix[decode->code_max - 2] == CODE_NONE) { + decode->prefix[decode->code_max - 2] = decode->code_last; + + /* Only allowed if code is exactly the running code: + * In that case code = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + decode->suffix[decode->code_max - 2] = getPrefix(decode, code == decode->code_max - 2 ? decode->code_last : code); + } + decode->code_last = code; + } + return cnt; +} + +/** + * Read the info on a frame. + * + * Pre: The file position is at the start of the frame. + */ +static gdispImageError initFrame(gdispImage *img) { + gdispImagePrivate * priv; + imgcache * cache; + uint8_t blocktype; + uint8_t blocksz; + + priv = img->priv; + + // Save the dispose info from the existing frame + priv->dispose.flags = priv->frame.flags; + priv->dispose.paltrans = priv->frame.paltrans; + priv->dispose.x = priv->frame.x; + priv->dispose.y = priv->frame.y; + priv->dispose.width = priv->frame.width; + priv->dispose.height = priv->frame.height; + + // Check for a cached version of this image + for(cache=priv->cache; cache && cache->frame.posstart <= (size_t)gfileGetPos(img->f); cache=cache->next) { + if (cache->frame.posstart == (size_t)gfileGetPos(img->f)) { + priv->frame = cache->frame; + priv->curcache = cache; + return GDISP_IMAGE_ERR_OK; + } + } + + // Get ready for a new image + priv->curcache = 0; + priv->frame.posstart = gfileGetPos(img->f); + priv->frame.flags = 0; + priv->frame.delay = 0; + priv->frame.palsize = 0; + + // Process blocks until we reach the image descriptor + while(1) { + if (gfileRead(img->f, &blocktype, 1) != 1) + return GDISP_IMAGE_ERR_BADDATA; + + switch(blocktype) { + case 0x2C: //',' - IMAGE_DESC_RECORD_TYPE; + // Read the Image Descriptor + if (gfileRead(img->f, priv->buf, 9) != 9) + return GDISP_IMAGE_ERR_BADDATA; + priv->frame.x = *(uint16_t *)(((uint8_t *)priv->buf)+0); + CONVERT_FROM_WORD_LE(priv->frame.x); + priv->frame.y = *(uint16_t *)(((uint8_t *)priv->buf)+2); + CONVERT_FROM_WORD_LE(priv->frame.y); + priv->frame.width = *(uint16_t *)(((uint8_t *)priv->buf)+4); + CONVERT_FROM_WORD_LE(priv->frame.width); + priv->frame.height = *(uint16_t *)(((uint8_t *)priv->buf)+6); + CONVERT_FROM_WORD_LE(priv->frame.height); + if (((uint8_t *)priv->buf)[8] & 0x80) // Local color table? + priv->frame.palsize = 2 << (((uint8_t *)priv->buf)[8] & 0x07); + if (((uint8_t *)priv->buf)[8] & 0x40) // Interlaced? + priv->frame.flags |= GIFL_INTERLACE; + + // We are ready to go for the actual palette read and image decode + priv->frame.pospal = gfileGetPos(img->f); + priv->frame.posimg = priv->frame.pospal+priv->frame.palsize*3; + priv->frame.posend = 0; + + // Mark this as an animated image if more than 1 frame. + if (priv->frame.posstart != priv->frame0pos) + img->flags |= GDISP_IMAGE_FLG_ANIMATED; + return GDISP_IMAGE_ERR_OK; + + case 0x21: //'!' - EXTENSION_RECORD_TYPE; + // Read the extension type + if (gfileRead(img->f, &blocktype, 1) != 1) + return GDISP_IMAGE_ERR_BADDATA; + + switch(blocktype) { + case 0xF9: // EXTENSION - Graphics Control Block + // Read the GCB + if (gfileRead(img->f, priv->buf, 6) != 6) + return GDISP_IMAGE_ERR_BADDATA; + // Check we have read a 4 byte data block and a data block terminator (0) + if (((uint8_t *)priv->buf)[0] != 4 || ((uint8_t *)priv->buf)[5] != 0) + return GDISP_IMAGE_ERR_BADDATA; + // Process the flags + switch(((uint8_t *)priv->buf)[1] & 0x1C) { + case 0x00: case 0x04: break; // Dispose = do nothing + case 0x08: priv->frame.flags |= GIFL_DISPOSECLEAR; break; // Dispose = clear + case 0x0C: case 0x10: priv->frame.flags |= GIFL_DISPOSEREST; break; // Dispose = restore. Value 0x10 is a hack for bad encoders + default: return GDISP_IMAGE_ERR_UNSUPPORTED; + } + if (((uint8_t *)priv->buf)[1] & 0x01) { + priv->frame.flags |= GIFL_TRANSPARENT; + img->flags |= GDISP_IMAGE_FLG_TRANSPARENT; // We set this but never clear it + } + if (((uint8_t *)priv->buf)[1] & 0x02) // Wait for user input? + img->flags |= GDISP_IMAGE_FLG_MULTIPAGE; + else + img->flags &= ~GDISP_IMAGE_FLG_MULTIPAGE; + // Process frame delay and the transparent color (if any) + priv->frame.delay = *(uint16_t *)(((uint8_t *)priv->buf)+2); + CONVERT_FROM_WORD_LE(priv->frame.delay); + priv->frame.paltrans = ((uint8_t *)priv->buf)[4]; + break; + + case 0xFF: // EXTENSION - Application + // We only handle this for the special Netscape loop counter for animation + if (priv->flags & GIF_LOOP) + goto skipdatablocks; + // Read the Application header + if (gfileRead(img->f, priv->buf, 16) != 16) + return GDISP_IMAGE_ERR_BADDATA; + // Check we have read a 11 byte data block + if (((uint8_t *)priv->buf)[0] != 11 && ((uint8_t *)priv->buf)[12] != 3) + return GDISP_IMAGE_ERR_BADDATA; + // Check the vendor + if (((uint8_t *)priv->buf)[1] == 'N' && ((uint8_t *)priv->buf)[2] == 'E' && ((uint8_t *)priv->buf)[3] == 'T' + && ((uint8_t *)priv->buf)[4] == 'S' && ((uint8_t *)priv->buf)[5] == 'C' && ((uint8_t *)priv->buf)[6] == 'A' + && ((uint8_t *)priv->buf)[7] == 'P' && ((uint8_t *)priv->buf)[8] == 'E' && ((uint8_t *)priv->buf)[9] == '2' + && ((uint8_t *)priv->buf)[10] == '.' && ((uint8_t *)priv->buf)[11] == '0') { + if (((uint8_t *)priv->buf)[13] == 1) { + priv->loops = *(uint16_t *)(((uint8_t *)priv->buf)+14); + CONVERT_FROM_WORD_LE(priv->loops); + priv->flags |= GIF_LOOP; + if (!priv->loops) + priv->flags |= GIF_LOOPFOREVER; + } + } + goto skipdatablocks; + + case 0x01: // EXTENSION - Plain Text (Graphics Rendering) + case 0xFE: // EXTENSION - Comment + default: + // 0x00-0x7F (0-127) are the Graphic Rendering blocks + if (blocktype <= 0x7F) + return GDISP_IMAGE_ERR_UNSUPPORTED; + // 0x80-0xF9 (128-249) are the Control blocks + // 0xFA-0xFF (250-255) are the Special Purpose blocks + // We don't understand this extension - just skip it by skipping data blocks + skipdatablocks: + while(1) { + if (gfileRead(img->f, &blocksz, 1) != 1) + return GDISP_IMAGE_ERR_BADDATA; + if (!blocksz) + break; + gfileSetPos(img->f, gfileGetPos(img->f) + blocksz); + } + break; + } + break; + + case 0x3B: //';' - TERMINATE_RECORD_TYPE; + // Are we an looping animation + if (!(priv->flags & GIF_LOOP)) + return GDISP_IMAGE_EOF; + if (!(priv->flags & GIF_LOOPFOREVER)) { + if (!priv->loops) + return GDISP_IMAGE_EOF; + priv->loops--; + } + + // Seek back to frame0 + gfileSetPos(img->f, priv->frame0pos); + return GDISP_IMAGE_LOOP; + + default: // UNDEFINED_RECORD_TYPE; + return GDISP_IMAGE_ERR_UNSUPPORTED; + } + } +} + +void gdispImageClose_GIF(gdispImage *img) { + gdispImagePrivate * priv; + imgcache * cache; + imgcache * ncache; + + priv = img->priv; + if (priv) { + // Free any stored frames + cache = priv->cache; + while(cache) { + ncache = cache->next; + gdispImageFree(img, (void *)cache, sizeof(imgcache)+cache->frame.width*cache->frame.height+cache->frame.palsize*sizeof(color_t)); + cache = ncache; + } + if (priv->palette) + gdispImageFree(img, (void *)priv->palette, priv->palsize*sizeof(color_t)); + gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); + img->priv = 0; + } +} + +gdispImageError gdispImageOpen_GIF(gdispImage *img) { + gdispImagePrivate *priv; + uint8_t hdr[6]; + uint16_t aword; + + /* Read the file identifier */ + if (gfileRead(img->f, hdr, 6) != 6) + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + /* Process the GIFFILEHEADER structure */ + + if (hdr[0] != 'G' || hdr[1] != 'I' || hdr[2] != 'F' + || hdr[3] != '8' || (hdr[4] != '7' && hdr[4] != '9') || hdr[5] != 'a') + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + /* We know we are a GIF format image */ + img->flags = 0; + + /* Allocate our private area */ + if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) + return GDISP_IMAGE_ERR_NOMEMORY; + + /* Initialise the essential bits in the private area */ + priv = img->priv; + priv->flags = 0; + priv->palsize = 0; + priv->palette = 0; + priv->frame.flags = 0; + priv->cache = 0; + priv->curcache = 0; + + /* Process the Screen Descriptor structure */ + + // Read the screen descriptor + if (gfileRead(img->f, priv->buf, 7) != 7) + goto baddatacleanup; + // Get the width + img->width = *(uint16_t *)(((uint8_t *)priv->buf)+0); + CONVERT_FROM_WORD_LE(img->width); + // Get the height + img->height = *(uint16_t *)(((uint8_t *)priv->buf)+2); + CONVERT_FROM_WORD_LE(img->height); + if (((uint8_t *)priv->buf)[4] & 0x80) { + // Global color table + priv->palsize = 2 << (((uint8_t *)priv->buf)[4] & 0x07); + // Allocate the global palette + if (!(priv->palette = (color_t *)gdispImageAlloc(img, priv->palsize*sizeof(color_t)))) + goto nomemcleanup; + // Read the global palette + for(aword = 0; aword < priv->palsize; aword++) { + if (gfileRead(img->f, &priv->buf, 3) != 3) + goto baddatacleanup; + priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[0], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[2]); + } + } + priv->bgcolor = ((uint8_t *)priv->buf)[5]; + + // Save the fram0pos + priv->frame0pos = gfileGetPos(img->f); + + // Read the first frame descriptor + switch(initFrame(img)) { + case GDISP_IMAGE_ERR_OK: // Everything OK + img->type = GDISP_IMAGE_TYPE_GIF; + return GDISP_IMAGE_ERR_OK; + case GDISP_IMAGE_ERR_UNSUPPORTED: // Unsupported + gdispImageClose_GIF(img); // Clean up the private data area + return GDISP_IMAGE_ERR_UNSUPPORTED; + case GDISP_IMAGE_ERR_NOMEMORY: // Out of Memory + nomemcleanup: + gdispImageClose_GIF(img); // Clean up the private data area + return GDISP_IMAGE_ERR_NOMEMORY; + case GDISP_IMAGE_EOF: // We should have a frame but we don't seem to + case GDISP_IMAGE_LOOP: // We should have a frame but we don't seem to + case GDISP_IMAGE_ERR_BADDATA: // Oops - something wrong with the data + default: + baddatacleanup: + gdispImageClose_GIF(img); // Clean up the private data area + return GDISP_IMAGE_ERR_BADDATA; + } +} + +gdispImageError gdispImageCache_GIF(gdispImage *img) { + gdispImagePrivate * priv; + imgcache * cache; + imgdecode * decode; + uint8_t * p; + uint8_t * q; + coord_t mx, my; + uint16_t cnt; + + /* If we are already cached - just return OK */ + priv = img->priv; + if (priv->curcache) + return GDISP_IMAGE_ERR_OK; + + /* We need to allocate the frame, the palette and bits for the image */ + if (!(cache = (imgcache *)gdispImageAlloc(img, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height))) + return GDISP_IMAGE_ERR_NOMEMORY; + + /* Initialise the cache */ + decode = 0; + cache->frame = priv->frame; + cache->imagebits = (uint8_t *)(cache+1) + cache->frame.palsize*sizeof(color_t); + cache->next = 0; + + /* Start the decode */ + switch(startDecode(img)) { + case GDISP_IMAGE_ERR_OK: break; + case GDISP_IMAGE_ERR_NOMEMORY: goto nomemcleanup; + case GDISP_IMAGE_ERR_BADDATA: + default: goto baddatacleanup; + } + decode = priv->decode; + + // Save the palette + if (cache->frame.palsize) { + cache->palette = (color_t *)(cache+1); + + /* Copy the local palette into the cache */ + for(cnt = 0; cnt < cache->frame.palsize; cnt++) + cache->palette[cnt] = decode->palette[cnt]; + } else + cache->palette = priv->palette; + + // Check for interlacing + cnt = 0; + if (cache->frame.flags & GIFL_INTERLACE) { + // Every 8th row starting at row 0 + for(p=cache->imagebits, my=0; my < cache->frame.height; my+=8, p += cache->frame.width*7) { + for(mx=0; mx < cache->frame.width; mx++) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + while(cnt < sizeof(decode->buf)) + decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; + } + q = decode->buf; + } + *p++ = *q++; + cnt--; + } + } + // Every 8th row starting at row 4 + for(p=cache->imagebits+cache->frame.width*4, my=4; my < cache->frame.height; my+=8, p += cache->frame.width*7) { + for(mx=0; mx < cache->frame.width; mx++) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + while(cnt < sizeof(decode->buf)) + decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; + } + q = decode->buf; + } + *p++ = *q++; + cnt--; + } + } + // Every 4th row starting at row 2 + for(p=cache->imagebits+cache->frame.width*2, my=2; my < cache->frame.height; my+=4, p += cache->frame.width*3) { + for(mx=0; mx < cache->frame.width; mx++) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + while(cnt < sizeof(decode->buf)) + decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; + } + q = decode->buf; + } + *p++ = *q++; + cnt--; + } + } + // Every 2nd row starting at row 1 + for(p=cache->imagebits+cache->frame.width, my=1; my < cache->frame.height; my+=2, p += cache->frame.width) { + for(mx=0; mx < cache->frame.width; mx++) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + while(cnt < sizeof(decode->buf)) + decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; + } + q = decode->buf; + } + *p++ = *q++; + cnt--; + } + } + } else { + // Every row in sequence + p=cache->imagebits; + for(my=0; my < cache->frame.height; my++) { + for(mx=0; mx < cache->frame.width; mx++) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + while(cnt < sizeof(decode->buf)) + decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; + } + q = decode->buf; + } + *p++ = *q++; + cnt--; + } + } + } + // We could be pedantic here but extra bytes won't hurt us + while(getbytes(img)); + priv->frame.posend = cache->frame.posend = gfileGetPos(img->f); + + // Save everything + priv->curcache = cache; + if (!priv->cache) + priv->cache = cache; + else if (priv->cache->frame.posstart > cache->frame.posstart) { + cache->next = priv->cache; + priv->cache = cache; + } else { + imgcache *pc; + + for(pc = priv->cache; pc; pc = pc->next) { + if (!pc->next || pc->next->frame.posstart > cache->frame.posstart) { + cache->next = pc->next; + pc->next = cache; + break; + } + } + } + stopDecode(img); + return GDISP_IMAGE_ERR_OK; + +nomemcleanup: + stopDecode(img); + gdispImageFree(img, cache, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height); + return GDISP_IMAGE_ERR_NOMEMORY; + +baddatacleanup: + stopDecode(img); + gdispImageFree(img, cache, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height); + return GDISP_IMAGE_ERR_BADDATA; +} + +gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { + gdispImagePrivate * priv; + imgdecode * decode; + uint8_t * q = 0; + coord_t mx, my, fx, fy; + uint16_t cnt, gcnt; + uint8_t col; + + priv = img->priv; + + /* Handle previous frame disposing */ + if (priv->dispose.flags & (GIFL_DISPOSECLEAR|GIFL_DISPOSEREST)) { + // Clip to the disposal area - clip area = mx,my -> fx, fy (sx,sy,cx,cy are unchanged) + mx = priv->dispose.x; + my = priv->dispose.y; + fx = priv->dispose.x+priv->dispose.width; + fy = priv->dispose.y+priv->dispose.height; + if (sx > mx) mx = sx; + if (sy > my) my = sy; + if (sx+cx <= fx) fx = sx+cx; + if (sy+cy <= fy) fy = sy+cy; + if (fx > mx && fy > my) { + // We only support clearing (not restoring). The specification says that we are allowed to do this. + // Calculate the bgcolor + // The spec says to restore the backgound color (priv->bgcolor) but in practice if there is transparency + // image decoders tend to assume that a restore to the transparent color is required instead + if (((priv->dispose.flags & GIFL_TRANSPARENT) /*&& priv->dispose.paltrans == priv->bgcolor*/) || priv->bgcolor >= priv->palsize) + gdispGFillArea(g, x+mx-sx, y+my-sy, fx-mx, fy-my, img->bgcolor); + else + gdispGFillArea(g, x+mx-sx, y+my-sy, fx-mx, fy-my, priv->palette[priv->bgcolor]); + } + } + + /* Clip to just this frame - clip area = sx,sy -> fx, fy */ + fx = priv->frame.x+priv->frame.width; + fy = priv->frame.y+priv->frame.height; + if (sx >= fx || sy >= fy || sx+cx < priv->frame.x || sy+cy < priv->frame.y) return GDISP_IMAGE_ERR_OK; + if (sx < priv->frame.x) { mx = priv->frame.x - sx; x += mx; cx -= mx; sx = priv->frame.x; } + if (sy < priv->frame.y) { my = priv->frame.y - sy; y += my; cy -= my; sy = priv->frame.y; } + if (sx+cx > fx) cx = fx-sx; + if (sy+cy > fy) cy = fy-sy; + + // Make sx, sy relative to this frame so we are not adding priv->frame.x & priv->frame.y each time + sx -= priv->frame.x; sy -= priv->frame.y; + fx = sx + cx; + fy = sy + cy; + + /* Draw from the image cache - if it exists */ + if (priv->curcache) { + imgcache * cache; + + cache = priv->curcache; + q = cache->imagebits+priv->frame.width*sy+sx; + + for(my=sy; my < fy; my++, q += priv->frame.width - cx) { + for(gcnt=0, mx=sx, cnt=0; mx < fx; mx++) { + col = *q++; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = cache->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + + return GDISP_IMAGE_ERR_OK; + } + + /* Start the decode */ + switch(startDecode(img)) { + case GDISP_IMAGE_ERR_OK: break; + case GDISP_IMAGE_ERR_NOMEMORY: return GDISP_IMAGE_ERR_NOMEMORY; + case GDISP_IMAGE_ERR_BADDATA: + default: return GDISP_IMAGE_ERR_BADDATA; + } + decode = priv->decode; + + // Check for interlacing + cnt = 0; + if (priv->frame.flags & GIFL_INTERLACE) { + // Every 8th row starting at row 0 + for(my=0; my < priv->frame.height; my+=8) { + for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + mx++; + break; + } + q = decode->buf; + } + if (my >= sy && my < fy && mx >= sx && mx < fx) { + col = *q; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = decode->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + continue; + } + // We have finished the visible area - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + // Every 8th row starting at row 4 + for(my=4; my < priv->frame.height; my+=8) { + for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + mx++; + break; + } + q = decode->buf; + } + if (my >= sy && my < fy && mx >= sx && mx < fx) { + col = *q; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = decode->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + continue; + } + // We have finished the visible area - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + // Every 4th row starting at row 2 + for(my=2; my < priv->frame.height; my+=4) { + for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + mx++; + break; + } + q = decode->buf; + } + if (my >= sy && my < fy && mx >= sx && mx < fx) { + col = *q; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = decode->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + continue; + } + // We have finished the visible area - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + // Every 2nd row starting at row 1 + for(my=1; my < priv->frame.height; my+=2) { + for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + mx++; + break; + } + q = decode->buf; + } + if (my >= sy && my < fy && mx >= sx && mx < fx) { + col = *q; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = decode->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + continue; + } + // We have finished the visible area - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + } else { + // Every row in sequence + for(my=0; my < priv->frame.height; my++) { + for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + mx++; + break; + } + q = decode->buf; + } + if (my >= sy && my < fy && mx >= sx && mx < fx) { + col = *q; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = decode->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + continue; + } + // We have finished the visible area - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + } + // We could be pedantic here but extra bytes won't hurt us + while (getbytes(img)); + priv->frame.posend = gfileGetPos(img->f); + + stopDecode(img); + return GDISP_IMAGE_ERR_OK; + +baddatacleanup: + stopDecode(img); + return GDISP_IMAGE_ERR_BADDATA; +} + +delaytime_t gdispImageNext_GIF(gdispImage *img) { + gdispImagePrivate * priv; + delaytime_t delay; + uint8_t blocksz; + + priv = img->priv; + + // Save the delay and convert to millisecs + delay = (delaytime_t)priv->frame.delay * 10; + + // We need to get to the end of this frame + if (!priv->frame.posend) { + // We don't know where the end of the frame is yet - find it! + gfileSetPos(img->f, priv->frame.posimg+1); // Skip the code size byte too + while(1) { + if (gfileRead(img->f, &blocksz, 1) != 1) + return TIME_INFINITE; + if (!blocksz) + break; + gfileSetPos(img->f, gfileGetPos(img->f) + blocksz); + } + priv->frame.posend = gfileGetPos(img->f); + } + + // Seek to the end of this frame + gfileSetPos(img->f, priv->frame.posend); + + // Read the next frame descriptor + for(blocksz=0; blocksz < 2; blocksz++) { // 2 loops max to prevent cycling forever with a bad file + switch(initFrame(img)) { + case GDISP_IMAGE_ERR_OK: // Everything OK + return delay; + case GDISP_IMAGE_LOOP: // Back to the beginning + break; + case GDISP_IMAGE_EOF: // The real End-Of-File + case GDISP_IMAGE_ERR_BADDATA: // Oops - something wrong with the data + case GDISP_IMAGE_ERR_NOMEMORY: // Out of Memory + case GDISP_IMAGE_ERR_UNSUPPORTED: // Unsupported + default: + return TIME_INFINITE; + } + } + return TIME_INFINITE; +} + +#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 new file mode 100644 index 00000000..2f6a9392 --- /dev/null +++ b/src/gdisp/gdisp_image_jpg.c @@ -0,0 +1,19 @@ +/* + * 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/image_jpg.c + * @brief GDISP native image code. + */ +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG + +#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 new file mode 100644 index 00000000..81344642 --- /dev/null +++ b/src/gdisp/gdisp_image_native.c @@ -0,0 +1,145 @@ +/* + * 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/image_native.c + * @brief GDISP native image code. + */ +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_NATIVE + +/** + * How big a pixel array to allocate for blitting + * Bigger is faster but uses more RAM. + */ +#define BLIT_BUFFER_SIZE 32 + +#define HEADER_SIZE 8 +#define FRAME0POS (HEADER_SIZE) + +/** + * Helper Routines Needed + */ +void *gdispImageAlloc(gdispImage *img, size_t sz); +void gdispImageFree(gdispImage *img, void *ptr, size_t sz); + +typedef struct gdispImagePrivate { + pixel_t *frame0cache; + pixel_t buf[BLIT_BUFFER_SIZE]; + } gdispImagePrivate; + +void gdispImageClose_NATIVE(gdispImage *img) { + if (img->priv) { + if (img->priv->frame0cache) + gdispImageFree(img, (void *)img->priv->frame0cache, img->width * img->height * sizeof(pixel_t)); + gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); + img->priv = 0; + } +} + +gdispImageError gdispImageOpen_NATIVE(gdispImage *img) { + uint8_t hdr[HEADER_SIZE]; + + /* Read the 8 byte header */ + if (gfileRead(img->f, hdr, 8) != 8) + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + if (hdr[0] != 'N' || hdr[1] != 'I') + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + if (hdr[6] != GDISP_PIXELFORMAT/256 || hdr[7] != (GDISP_PIXELFORMAT & 0xFF)) + return GDISP_IMAGE_ERR_UNSUPPORTED; // Unsupported pixel format + + /* We know we are a native format image */ + img->flags = 0; + img->width = (((uint16_t)hdr[2])<<8) | (hdr[3]); + img->height = (((uint16_t)hdr[4])<<8) | (hdr[5]); + if (img->width < 1 || img->height < 1) + return GDISP_IMAGE_ERR_BADDATA; + if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) + return GDISP_IMAGE_ERR_NOMEMORY; + img->priv->frame0cache = 0; + + img->type = GDISP_IMAGE_TYPE_NATIVE; + return GDISP_IMAGE_ERR_OK; +} + +gdispImageError gdispImageCache_NATIVE(gdispImage *img) { + size_t len; + + /* If we are already cached - just return OK */ + if (img->priv->frame0cache) + return GDISP_IMAGE_ERR_OK; + + /* We need to allocate the cache */ + len = img->width * img->height * sizeof(pixel_t); + img->priv->frame0cache = (pixel_t *)gdispImageAlloc(img, len); + if (!img->priv->frame0cache) + return GDISP_IMAGE_ERR_NOMEMORY; + + /* Read the entire bitmap into cache */ + gfileSetPos(img->f, FRAME0POS); + if (gfileRead(img->f, img->priv->frame0cache, len) != len) + return GDISP_IMAGE_ERR_BADDATA; + + return GDISP_IMAGE_ERR_OK; +} + +gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { + coord_t mx, mcx; + size_t pos, len; + + /* Check some reasonableness */ + if (sx >= img->width || sy >= img->height) return GDISP_IMAGE_ERR_OK; + if (sx + cx > img->width) cx = img->width - sx; + if (sy + cy > img->height) cy = img->height - sy; + + /* Draw from the image cache - if it exists */ + if (img->priv->frame0cache) { + gdispGBlitArea(g, x, y, cx, cy, sx, sy, img->width, img->priv->frame0cache); + return GDISP_IMAGE_ERR_OK; + } + + /* For this image decoder we cheat and just seek straight to the region we want to display */ + pos = FRAME0POS + (img->width * sy + sx) * sizeof(pixel_t); + + /* Cycle through the lines */ + for(;cy;cy--, y++) { + /* Move to the start of the line */ + gfileSetPos(img->f, pos); + + /* Draw the line in chunks using BitBlt */ + for(mx = x, mcx = cx; mcx > 0; mcx -= len, mx += len) { + // Read the data + len = gfileRead(img->f, + img->priv->buf, + mcx > BLIT_BUFFER_SIZE ? (BLIT_BUFFER_SIZE*sizeof(pixel_t)) : (mcx * sizeof(pixel_t))) + / sizeof(pixel_t); + if (!len) + return GDISP_IMAGE_ERR_BADDATA; + + /* Blit the chunk of data */ + gdispGBlitArea(g, mx, y, len, 1, 0, 0, len, img->priv->buf); + } + + /* Get the position for the start of the next line */ + pos += img->width*sizeof(pixel_t); + } + + return GDISP_IMAGE_ERR_OK; +} + +delaytime_t gdispImageNext_NATIVE(gdispImage *img) { + (void) img; + + /* No more frames/pages */ + return TIME_INFINITE; +} + +#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 new file mode 100644 index 00000000..f35174d9 --- /dev/null +++ b/src/gdisp/gdisp_image_png.c @@ -0,0 +1,19 @@ +/* + * 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/image_png.c + * @brief GDISP native image code. + */ +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_PNG + +#error "PNG support not implemented yet" + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_PNG */ +/** @} */ diff --git a/src/gdisp/image.c b/src/gdisp/image.c deleted file mode 100644 index e2b7d758..00000000 --- a/src/gdisp/image.c +++ /dev/null @@ -1,233 +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/image.c - * @brief GDISP generic image code. - * - * @defgroup Image Image - * @ingroup GDISP - */ -#include "gfx.h" - -#if GFX_USE_GDISP && GDISP_NEED_IMAGE - -#if GDISP_NEED_IMAGE_NATIVE - extern gdispImageError gdispImageOpen_NATIVE(gdispImage *img); - extern void gdispImageClose_NATIVE(gdispImage *img); - extern gdispImageError gdispImageCache_NATIVE(gdispImage *img); - extern gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - extern delaytime_t gdispImageNext_NATIVE(gdispImage *img); -#endif - -#if GDISP_NEED_IMAGE_GIF - extern gdispImageError gdispImageOpen_GIF(gdispImage *img); - extern void gdispImageClose_GIF(gdispImage *img); - extern gdispImageError gdispImageCache_GIF(gdispImage *img); - extern gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - extern delaytime_t gdispImageNext_GIF(gdispImage *img); -#endif - -#if GDISP_NEED_IMAGE_BMP - extern gdispImageError gdispImageOpen_BMP(gdispImage *img); - extern void gdispImageClose_BMP(gdispImage *img); - extern gdispImageError gdispImageCache_BMP(gdispImage *img); - extern gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - extern delaytime_t gdispImageNext_BMP(gdispImage *img); -#endif - -#if GDISP_NEED_IMAGE_JPG - extern gdispImageError gdispImageOpen_JPG(gdispImage *img); - extern void gdispImageClose_JPG(gdispImage *img); - extern gdispImageError gdispImageCache_JPG(gdispImage *img); - extern gdispImageError gdispGImageDraw_JPG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - extern delaytime_t gdispImageNext_JPG(gdispImage *img); -#endif - -#if GDISP_NEED_IMAGE_PNG - extern gdispImageError gdispImageOpen_PNG(gdispImage *img); - extern void gdispImageClose_PNG(gdispImage *img); - extern gdispImageError gdispImageCache_PNG(gdispImage *img); - extern gdispImageError gdispGImageDraw_PNG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - extern delaytime_t gdispImageNext_PNG(gdispImage *img); -#endif - -/* The structure defining the routines for image drawing */ -typedef struct gdispImageHandlers { - gdispImageError (*open)(gdispImage *img); /* The open function */ - void (*close)(gdispImage *img); /* The close function */ - gdispImageError (*cache)(gdispImage *img); /* The cache function */ - gdispImageError (*draw)(GDisplay *g, - gdispImage *img, - coord_t x, coord_t y, - coord_t cx, coord_t cy, - coord_t sx, coord_t sy); /* The draw function */ - delaytime_t (*next)(gdispImage *img); /* The next frame function */ -} gdispImageHandlers; - -static gdispImageHandlers ImageHandlers[] = { - #if GDISP_NEED_IMAGE_NATIVE - { gdispImageOpen_NATIVE, gdispImageClose_NATIVE, - gdispImageCache_NATIVE, gdispGImageDraw_NATIVE, gdispImageNext_NATIVE, - }, - #endif - #if GDISP_NEED_IMAGE_GIF - { gdispImageOpen_GIF, gdispImageClose_GIF, - gdispImageCache_GIF, gdispGImageDraw_GIF, gdispImageNext_GIF, - }, - #endif - #if GDISP_NEED_IMAGE_BMP - { gdispImageOpen_BMP, gdispImageClose_BMP, - gdispImageCache_BMP, gdispGImageDraw_BMP, gdispImageNext_BMP, - }, - #endif - #if GDISP_NEED_IMAGE_JPG - { gdispImageOpen_JPG, gdispImageClose_JPG, - gdispImageCache_JPG, gdispGImageDraw_JPG, gdispImageNext_JPG, - }, - #endif - #if GDISP_NEED_IMAGE_PNG - { gdispImageOpen_PNG, gdispImageClose_PNG, - gdispImageCache_PNG, gdispGImageDraw_PNG, gdispImageNext_PNG, - }, - #endif -}; - -gdispImageError - DEPRECATED("Use gdispImageOpenGFile() instead") - gdispImageOpen(gdispImage *img) { - return gdispImageOpenGFile(img, img->f); -} - -#if GFILE_NEED_MEMFS - bool_t - DEPRECATED("Use gdispImageOpenMemory() instead") - gdispImageSetMemoryReader(gdispImage *img, const void *memimage) { - img->f = gfileOpenMemory((void *)memimage, "rb"); - return img->f != 0; - } -#endif - -#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX - bool_t - DEPRECATED("Use gdispImageOpenFile() instead") - gdispImageSetFileReader(gdispImage *img, const char *filename) { - img->f = gfileOpen(filename, "rb"); - return img->f != 0; - } -#endif - -#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS - bool_t - DEPRECATED("Use gdispImageOpenBaseFileStream() instead") - gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr) { - img->f = gfileOpenBaseFileStream(BaseFileStreamPtr, "rb"); - return img->f != 0; - } -#endif - -void gdispImageInit(gdispImage *img) { - img->type = GDISP_IMAGE_TYPE_UNKNOWN; -} - -gdispImageError gdispImageOpenGFile(gdispImage *img, GFILE *f) { - gdispImageError err; - - if (!f) - return GDISP_IMAGE_ERR_NOSUCHFILE; - img->f = f; - img->bgcolor = White; - for(img->fns = ImageHandlers; img->fns < ImageHandlers+sizeof(ImageHandlers)/sizeof(ImageHandlers[0]); img->fns++) { - err = img->fns->open(img); - if (err != GDISP_IMAGE_ERR_BADFORMAT) { - if ((err & GDISP_IMAGE_ERR_UNRECOVERABLE)) - goto unrecoverable; - - // Everything is possible - return err; - } - - // Try the next decoder - gfileSetPos(img->f, 0); - } - - err = GDISP_IMAGE_ERR_BADFORMAT; - img->type = GDISP_IMAGE_TYPE_UNKNOWN; - -unrecoverable: - gfileClose(img->f); - img->f = 0; - img->flags = 0; - img->fns = 0; - img->priv = 0; - return err; -} - -void gdispImageClose(gdispImage *img) { - if (img->fns) - img->fns->close(img); - gfileClose(img->f); - img->type = GDISP_IMAGE_TYPE_UNKNOWN; - img->flags = 0; - img->fns = 0; - img->priv = 0; -} - -bool_t gdispImageIsOpen(gdispImage *img) { - return img->type != GDISP_IMAGE_TYPE_UNKNOWN && img->fns != 0; -} - -void gdispImageSetBgColor(gdispImage *img, color_t bgcolor) { - img->bgcolor = bgcolor; -} - -gdispImageError gdispImageCache(gdispImage *img) { - if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; - return img->fns->cache(img); -} - -gdispImageError gdispGImageDraw(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { - if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; - return img->fns->draw(g, img, x, y, cx, cy, sx, sy); -} - -delaytime_t gdispImageNext(gdispImage *img) { - if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; - return img->fns->next(img); -} - -// Helper Routines -void *gdispImageAlloc(gdispImage *img, size_t sz) { - #if GDISP_NEED_IMAGE_ACCOUNTING - void *ptr; - - ptr = gfxAlloc(sz); - if (ptr) { - img->memused += sz; - if (img->memused > img->maxmemused) - img->maxmemused = img->memused; - } - return ptr; - #else - (void) img; - return gfxAlloc(sz); - #endif -} - -void gdispImageFree(gdispImage *img, void *ptr, size_t sz) { - #if GDISP_NEED_IMAGE_ACCOUNTING - gfxFree(ptr); - img->memused -= sz; - #else - (void) img; - (void) sz; - gfxFree(ptr); - #endif -} - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ -/** @} */ diff --git a/src/gdisp/image.h b/src/gdisp/image.h deleted file mode 100644 index 76e8bdea..00000000 --- a/src/gdisp/image.h +++ /dev/null @@ -1,315 +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/image.h - * @brief GDISP image header file. - * - * @defgroup Image Image - * @ingroup GDISP - * @{ - */ - -#ifndef _GDISP_IMAGE_H -#define _GDISP_IMAGE_H -#if (GFX_USE_GDISP && GDISP_NEED_IMAGE) || defined(__DOXYGEN__) - -/** - * @brief The type of image - */ -typedef uint16_t gdispImageType; - #define GDISP_IMAGE_TYPE_UNKNOWN 0 - #define GDISP_IMAGE_TYPE_NATIVE 1 - #define GDISP_IMAGE_TYPE_GIF 2 - #define GDISP_IMAGE_TYPE_BMP 3 - #define GDISP_IMAGE_TYPE_JPG 4 - #define GDISP_IMAGE_TYPE_PNG 5 - -/** - * @brief An image error code - */ -typedef uint16_t gdispImageError; - #define GDISP_IMAGE_ERR_OK 0 - #define GDISP_IMAGE_ERR_UNRECOVERABLE 0x8000 - #define GDISP_IMAGE_ERR_BADFORMAT (GDISP_IMAGE_ERR_UNRECOVERABLE+1) - #define GDISP_IMAGE_ERR_BADDATA (GDISP_IMAGE_ERR_UNRECOVERABLE+2) - #define GDISP_IMAGE_ERR_UNSUPPORTED (GDISP_IMAGE_ERR_UNRECOVERABLE+3) - #define GDISP_IMAGE_ERR_UNSUPPORTED_OK 3 - #define GDISP_IMAGE_ERR_NOMEMORY (GDISP_IMAGE_ERR_UNRECOVERABLE+4) - #define GDISP_IMAGE_ERR_NOSUCHFILE (GDISP_IMAGE_ERR_UNRECOVERABLE+5) - -/** - * @brief Image flags - */ -typedef uint16_t gdispImageFlags; - #define GDISP_IMAGE_FLG_TRANSPARENT 0x0001 /* The image has transparency */ - #define GDISP_IMAGE_FLG_ANIMATED 0x0002 /* The image has animation */ - #define GDISP_IMAGE_FLG_MULTIPAGE 0x0004 /* The image has multiple pages */ - -struct gdispImageIO; - -/** - * @brief An image IO close function - * - * @param[in] pio Pointer to the io structure - * @param[in] desc The descriptor. A filename or an image structure pointer. - * - */ -typedef void (*gdispImageIOCloseFn)(struct gdispImageIO *pio); - -/** - * @brief An image IO read function - * @returns The number of bytes actually read or 0 on error - * - * @param[in] pio Pointer to the io structure - * @param[in] buf Where the results should be placed - * @param[in] len The number of bytes to read - * - */ -typedef size_t (*gdispImageIOReadFn)(struct gdispImageIO *pio, void *buf, size_t len); - -/** - * @brief An image IO seek function - * - * @param[in] pio Pointer to the io structure - * @param[in] pos Which byte to seek to relative to the start of the "file". - * - */ -typedef void (*gdispImageIOSeekFn)(struct gdispImageIO *pio, size_t pos); - -typedef struct gdispImageIOFunctions { - gdispImageIOReadFn read; /* @< The function to read input */ - gdispImageIOSeekFn seek; /* @< The function to seek input */ - gdispImageIOCloseFn close; /* @< The function to close input */ - } gdispImageIOFunctions; - -/** - * @brief The structure defining the IO routines for image handling - */ -typedef struct gdispImageIO { - const void * fd; /* @< The "file" descriptor */ - size_t pos; /* @< The current "file" position */ - const gdispImageIOFunctions *fns; /* @< The current "file" functions */ -} gdispImageIO; - -/** - * @brief The structure for an image - */ -typedef struct gdispImage { - gdispImageType type; /* @< The image type */ - gdispImageFlags flags; /* @< The image flags */ - color_t bgcolor; /* @< The default background color */ - coord_t width, height; /* @< The image dimensions */ - GFILE * f; /* @< The underlying GFILE */ - #if GDISP_NEED_IMAGE_ACCOUNTING - uint32_t memused; /* @< How much RAM is currently allocated */ - uint32_t maxmemused; /* @< How much RAM has been allocated (maximum) */ - #endif - const struct gdispImageHandlers * fns; /* @< Don't mess with this! */ - struct gdispImagePrivate * priv; /* @< Don't mess with this! */ -} gdispImage; - -#ifdef __cplusplus -extern "C" { -#endif - - /* - * Deprecated Functions. - */ - gdispImageError DEPRECATED("Use gdispImageOpenGFile() instead") gdispImageOpen(gdispImage *img); - bool_t DEPRECATED("Use gdispImageOpenMemory() instead") gdispImageSetMemoryReader(gdispImage *img, const void *memimage); - #if GFX_USE_OS_CHIBIOS - bool_t DEPRECATED("Use gdispImageOpenBaseFileStream() instead") gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr); - #endif - #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX - bool_t DEPRECATED("Please use gdispImageOpenFile() instead") gdispImageSetFileReader(gdispImage *img, const char *filename); - #define gdispImageSetSimulFileReader(img, fname) gdispImageSetFileReader(img, fname) - #endif - - /** - * @brief Initialise a gdispImage object - * - * @param[in] img The image structure to initialise - * - */ - void gdispImageInit(gdispImage *img); - - /** - * @brief Open an image using an open GFILE and get it ready for drawing - * @details Determine the image format and get ready to decode the first image frame - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @param[in] img The image structure - * @param[in] f The open GFILE stream. - * - * @pre The GFILE must be open for reading. - * - * @note This determines which decoder to use and then initialises all other fields - * in the gdispImage structure. - * @note The image background color is set to White. - * @note There are three types of return - everything OK, partial success and unrecoverable - * failures. For everything OK it returns GDISP_IMAGE_ERR_OK. A partial success can - * be distinguished from a unrecoverable failure by testing the GDISP_IMAGE_ERR_UNRECOVERABLE - * bit in the error code. - * A partial success return code means an image can still be drawn but perhaps with - * reduced functionality eg only the first page of a multi-page image. - * @note @p gdispImageClose() should be called when finished with the image. This will close - * the image and its underlying GFILE file. Note that images opened with partial success - * (eg GDISP_IMAGE_ERR_UNSUPPORTED_OK) - * still need to be closed when you are finished with them. - */ - gdispImageError gdispImageOpenGFile(gdispImage *img, GFILE *f); - - /** - * @brief Open an image in a file and get it ready for drawing - * @details Determine the image format and get ready to decode the first image frame - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @pre You must have included the file-system support into GFILE that you want to use. - * - * @param[in] img The image structure - * @param[in] filename The filename to open - * - * @note This function just opens the GFILE using the filename and passes it to @p gdispImageOpenGFile(). - */ - #define gdispImageOpenFile(img, filename) gdispImageOpenGFile((img), gfileOpen((filename), "rb")) - - /** - * @brief Open an image in a ChibiOS basefilestream and get it ready for drawing - * @details Determine the image format and get ready to decode the first image frame - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @pre GFILE_NEED_CHIBIOSFS and GFX_USE_OS_CHIBIOS must be TRUE. This only makes sense on the ChibiOS - * operating system. - * - * @param[in] img The image structure - * @param[in] BaseFileStreamPtr A pointer to an open BaseFileStream - * - * @note This function just opens the GFILE using the basefilestream and passes it to @p gdispImageOpenGFile(). - */ - #define gdispImageOpenBaseFileStream(img, BaseFileStreamPtr) gdispImageOpenGFile((img), gfileOpenBaseFileStream((BaseFileStreamPtr), "rb")) - - /** - * @brief Open an image in memory and get it ready for drawing - * @details Determine the image format and get ready to decode the first image frame - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @pre GFILE_NEED_MEMFS must be TRUE - * - * @param[in] img The image structure - * @param[in] ptr A pointer to the image bytes in memory - * - * @note This function just opens the GFILE using the basefilestream and passes it to @p gdispImageOpenGFile(). - */ - #define gdispImageOpenMemory(img, ptr) gdispImageOpenGFile((img), gfileOpenMemory((void *)(ptr), "rb")) - - /** - * @brief Close an image and release any dynamically allocated working storage. - * - * @param[in] img The image structure - * - * @pre gdispImageOpenFile() must have returned successfully. - * - * @note Also calls the IO close function (if it hasn't already been called). - */ - void gdispImageClose(gdispImage *img); - - /** - * @brief Is an image open. - * @return TRUE if the image is currently open. - * - * @param[in] img The image structure - * - * @note Be careful with calling this on an uninitialized image structure as the image - * will contain random data which may be interpreted as meaning the image - * is open. Clearing the Image structure to 0's will guarantee the image - * is seen as being closed. - */ - bool_t gdispImageIsOpen(gdispImage *img); - - /** - * @brief Set the background color of the image. - * - * @param[in] img The image structure - * @param[in] bgcolor The background color to use - * - * @pre gdispImageOpen() must have returned successfully. - * - * @note This color is only used when an image has to restore part of the background before - * continuing with drawing that includes transparency eg some GIF animations. - */ - void gdispImageSetBgColor(gdispImage *img, color_t bgcolor); - - /** - * @brief Cache the image - * @details Decodes and caches the current frame into RAM. - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @param[in] img The image structure - * - * @pre gdispImageOpen() must have returned successfully. - * - * @note This can use a LOT of RAM! - * @note The decoder may choose to ignore the request for caching. If it does so it will - * return GDISP_IMAGE_ERR_UNSUPPORTED_OK. - * @note A fatal error here does not necessarily mean that drawing the image will fail. For - * example, a GDISP_IMAGE_ERR_NOMEMORY error simply means there isn't enough RAM to - * cache the image. - */ - gdispImageError gdispImageCache(gdispImage *img); - - /** - * @brief Draw the image - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @param[in] g The display to draw on - * @param[in] img The image structure - * @param[in] x,y The screen 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) - */ - gdispImageError gdispGImageDraw(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - #define gdispImageDraw(img,x,y,cx,cy,sx,sy) gdispGImageDraw(GDISP,img,x,y,cx,cy,sx,sy) - - /** - * @brief Prepare for the next frame/page in the image file. - * @return A time in milliseconds to keep displaying the current frame before trying to draw - * the next frame. Watch out for the special values TIME_IMMEDIATE and TIME_INFINITE. - * - * @param[in] img The image structure - * - * @pre gdispImageOpen() must have returned successfully. - * - * @note It will return TIME_IMMEDIATE if the first frame/page hasn't been drawn or if the next frame - * should be drawn immediately. - * @note It will return TIME_INFINITE if another image frame doesn't exist or an error has occurred. - * @note Images that support multiple pages (eg TIFF files) will return TIME_IMMEDIATE between pages - * and then TIME_INFINITE when there are no more pages. - * @note An image that displays a looped animation will never return TIME_INFINITE unless it - * gets an error. - * @note Calling gdispImageDraw() after getting a TIME_INFINITE will go back to drawing the first - * frame/page. - */ - delaytime_t gdispImageNext(gdispImage *img); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ -#endif /* _GDISP_IMAGE_H */ -/** @} */ - diff --git a/src/gdisp/image_bmp.c b/src/gdisp/image_bmp.c deleted file mode 100644 index 8ff40ca0..00000000 --- a/src/gdisp/image_bmp.c +++ /dev/null @@ -1,904 +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/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 - */ -void *gdispImageAlloc(gdispImage *img, size_t sz); -void gdispImageFree(gdispImage *img, void *ptr, size_t sz); - -/** - * How big a pixel array to allocate for blitting (in pixels) - * Bigger is faster but uses more RAM. - * This must be greater than 40 bytes and 32 pixels as we read our headers into this space as well - */ -#define BLIT_BUFFER_SIZE 32 - -/* - * Determining endianness as at compile time is not guaranteed or compiler portable. - * We use the best test we can. If we can't guarantee little endianness we do things the - * hard way. - */ -#define GUARANTEED_LITTLE_ENDIAN (!defined(SAFE_ENDIAN) && !defined(SAFE_ALIGNMENT) && (\ - (defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \ - || defined(__LITTLE_ENDIAN__) \ - || defined(__LITTLE_ENDIAN) \ - || defined(_LITTLE_ENDIAN) \ -/* || (1 == *(unsigned char *)&(const int){1})*/ \ - )) - - -/* This is a runtime test */ -static const uint8_t dwordOrder[4] = { 1, 2, 3, 4 }; - -#define isWordLittleEndian() (*(uint16_t *)&dwordOrder == 0x0201) -#define isDWordLittleEndian() (*(uint32_t *)&dwordOrder == 0x04030201) - -#if GUARANTEED_LITTLE_ENDIAN - /* These are fast routines for guaranteed little endian machines */ - #define CONVERT_FROM_WORD_LE(w) - #define CONVERT_FROM_DWORD_LE(dw) -#else - /* These are slower routines for when little endianness cannot be guaranteed at compile time */ - #define CONVERT_FROM_WORD_LE(w) { if (!isWordLittleEndian()) w = ((((uint16_t)(w))>>8)|(((uint16_t)(w))<<8)); } - #define CONVERT_FROM_DWORD_LE(dw) { if (!isDWordLittleEndian()) dw = (((uint32_t)(((const uint8_t *)(&dw))[0]))|(((uint32_t)(((const uint8_t *)(&dw))[1]))<<8)|(((uint32_t)(((const uint8_t *)(&dw))[2]))<<16)|(((uint32_t)(((const uint8_t *)(&dw))[3]))<<24)); } -#endif - -typedef struct gdispImagePrivate { - uint8_t bmpflags; - #define BMP_V2 0x01 // Version 2 (old) header format - #define BMP_V4 0x02 // Version 4 (alpha support) header format - #define BMP_PALETTE 0x04 // Uses a palette - #define BMP_COMP_RLE 0x08 // Uses RLE compression - #define BMP_COMP_MASK 0x10 // Uses mask & shift decoding - #define BMP_RLE_ENC 0x20 // Currently in RLE encoded run - #define BMP_RLE_ABS 0x40 // Currently in RLE absolute run - #define BMP_TOP_TO_BOTTOM 0x80 // Decodes bottom to top line - uint8_t bitsperpixel; -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - uint16_t palsize; - pixel_t *palette; -#endif -#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE - uint16_t rlerun; - uint8_t rlecode; -#endif -#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 - int8_t shiftred; - int8_t shiftgreen; - int8_t shiftblue; - int8_t shiftalpha; - uint32_t maskred; - uint32_t maskgreen; - uint32_t maskblue; - uint32_t maskalpha; -#endif - size_t frame0pos; - pixel_t *frame0cache; - pixel_t buf[BLIT_BUFFER_SIZE]; - } gdispImagePrivate; - -void gdispImageClose_BMP(gdispImage *img) { - if (img->priv) { -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - if (img->priv->palette) - gdispImageFree(img, (void *)img->priv->palette, img->priv->palsize*sizeof(color_t)); -#endif - if (img->priv->frame0cache) - gdispImageFree(img, (void *)img->priv->frame0cache, img->width*img->height*sizeof(pixel_t)); - gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); - img->priv = 0; - } -} - -gdispImageError gdispImageOpen_BMP(gdispImage *img) { - gdispImagePrivate *priv; - uint8_t hdr[2]; - uint16_t aword; - uint32_t adword; - uint32_t offsetColorTable; - - /* Read the file identifier */ - if (gfileRead(img->f, hdr, 2) != 2) - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - /* Process the BITMAPFILEHEADER structure */ - - /** - * We only accept Windows V2+ bitmaps. - * - we don't support OS/2 bitmaps, icons, pointers, or Windows V1 bitmaps. - */ - if (hdr[0] != 'B' || hdr[1] != 'M') - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - /* We know we are a BMP format image */ - img->flags = 0; - - /* Allocate our private area */ - if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) - return GDISP_IMAGE_ERR_NOMEMORY; - - /* Initialise the essential bits in the private area */ - priv = img->priv; - priv->frame0cache = 0; - priv->bmpflags = 0; -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - priv->palette = 0; -#endif - - /* Skip the size field and the 2 reserved fields */ - if (gfileRead(img->f, priv->buf, 8) != 8) - goto baddatacleanup; - - /* Get the offset to the bitmap data */ - if (gfileRead(img->f, &priv->frame0pos, 4) != 4) - goto baddatacleanup; - CONVERT_FROM_DWORD_LE(priv->frame0pos); - - /* Process the BITMAPCOREHEADER structure */ - - /* Get the offset to the colour data */ - if (gfileRead(img->f, &offsetColorTable, 4) != 4) - goto baddatacleanup; - CONVERT_FROM_DWORD_LE(offsetColorTable); - offsetColorTable += 14; // Add the size of the BITMAPFILEHEADER - - // Detect our bitmap version - if (offsetColorTable == 12+14) { - img->priv->bmpflags |= BMP_V2; - - // Read the header - if (gfileRead(img->f, priv->buf, 12-4) != 12-4) - goto baddatacleanup; - // Get the width - img->width = *(uint16_t *)(((uint8_t *)priv->buf)+0); - CONVERT_FROM_WORD_LE(img->width); - // Get the height - img->height = *(uint16_t *)(((uint8_t *)priv->buf)+2); - CONVERT_FROM_WORD_LE(img->height); - if (img->height < 0) { - img->priv->bmpflags |= BMP_TOP_TO_BOTTOM; - img->height = -img->height; - } - // Get the planes - aword = *(uint16_t *)(((uint8_t *)priv->buf)+4); - CONVERT_FROM_WORD_LE(aword); - if (aword != 1) - goto unsupportedcleanup; - // Get the bits per pixel - aword = *(uint16_t *)(((uint8_t *)priv->buf)+6); - CONVERT_FROM_WORD_LE(aword); - switch(aword) { -#if GDISP_NEED_IMAGE_BMP_1 - case 1: -#endif -#if GDISP_NEED_IMAGE_BMP_4 - case 4: -#endif -#if GDISP_NEED_IMAGE_BMP_8 - case 8: -#endif -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_8 - priv->bmpflags |= BMP_PALETTE; - priv->palsize = 1<bitsperpixel = aword; - - } else if (offsetColorTable >= 40+14) { - if (offsetColorTable > 40+14) - priv->bmpflags |= BMP_V4; - - // Read the header - if (gfileRead(img->f, priv->buf, 40-4) != 40-4) - goto baddatacleanup; - // Get the width - adword = *(uint32_t *)(((uint8_t *)priv->buf)+0); - CONVERT_FROM_DWORD_LE(adword); - if (adword > 32768) // This also picks up negative values - goto unsupportedcleanup; - img->width = adword; - // Get the height - adword = *(uint32_t *)(((uint8_t *)priv->buf)+4); - CONVERT_FROM_DWORD_LE(adword); - if ((int32_t)adword < 0) { // Negative test - priv->bmpflags |= BMP_TOP_TO_BOTTOM; - adword = -adword; - } - if (adword > 32768) - goto unsupportedcleanup; - img->height = adword; - // Get the planes - aword = *(uint16_t *)(((uint8_t *)priv->buf)+8); - CONVERT_FROM_WORD_LE(aword); - if (aword != 1) - goto unsupportedcleanup; - // Get the bits per pixel - aword = *(uint16_t *)(((uint8_t *)priv->buf)+10); - CONVERT_FROM_WORD_LE(aword); - switch(aword) { -#if GDISP_NEED_IMAGE_BMP_1 - case 1: -#endif -#if GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE - case 4: -#endif -#if GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - case 8: -#endif -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - priv->bmpflags |= BMP_PALETTE; - priv->palsize = 1<bitsperpixel = aword; - // Get the compression - adword = *(uint32_t *)(((uint8_t *)priv->buf)+12); - CONVERT_FROM_DWORD_LE(adword); - switch(adword) { - case 0: // BI_RGB - uncompressed - break; -#if GDISP_NEED_IMAGE_BMP_8_RLE - case 1: // BI_RLE8 compression - if (priv->bitsperpixel != 8) - goto unsupportedcleanup; - priv->bmpflags |= BMP_COMP_RLE; - break; -#endif -#if GDISP_NEED_IMAGE_BMP_4_RLE - case 2: // BI_RLE4 compression - if (priv->bitsperpixel != 4) - goto unsupportedcleanup; - priv->bmpflags |= BMP_COMP_RLE; - break; -#endif -#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 - case 3: // BI_BITFIELDS decoding - if (priv->bitsperpixel < 16 || priv->bitsperpixel == 24) - goto unsupportedcleanup; - priv->bmpflags |= BMP_COMP_MASK; - if (priv->bmpflags & BMP_V4) // V4 stored the masks in the header - offsetColorTable = 40+14; - break; -#endif - default: - goto unsupportedcleanup; - } - priv->bitsperpixel = aword; -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - // Get the actual colors used - adword = *(uint32_t *)(((uint8_t *)priv->buf)+28); - CONVERT_FROM_DWORD_LE(adword); - if (adword && adword < priv->palsize) - priv->palsize = adword; -#endif - } else - goto baddatacleanup; - -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - /* Load the palette tables */ - if (priv->bmpflags & BMP_PALETTE) { - gfileSetPos(img->f, offsetColorTable); - - if (!(priv->palette = (color_t *)gdispImageAlloc(img, priv->palsize*sizeof(color_t)))) - return GDISP_IMAGE_ERR_NOMEMORY; - if (priv->bmpflags & BMP_V2) { - for(aword = 0; aword < priv->palsize; aword++) { - if (gfileRead(img->f, &priv->buf, 3) != 3) goto baddatacleanup; - priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[2], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[0]); - } - } else { - for(aword = 0; aword < priv->palsize; aword++) { - if (gfileRead(img->f, &priv->buf, 4) != 4) goto baddatacleanup; - priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[2], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[0]); - } - } - - } -#endif - -#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 - /* Load the bit masks */ - if (priv->bmpflags & BMP_COMP_MASK) { - gfileSetPos(img->f, offsetColorTable); - if (gfileRead(img->f, &priv->maskred, 4) != 4) goto baddatacleanup; - CONVERT_FROM_DWORD_LE(priv->maskred); - if (gfileRead(img->f, &priv->maskgreen, 4) != 4) goto baddatacleanup; - CONVERT_FROM_DWORD_LE(priv->maskgreen); - if (gfileRead(img->f, &priv->maskblue, 4) != 4) goto baddatacleanup; - CONVERT_FROM_DWORD_LE(priv->maskblue); - if (priv->bmpflags & BMP_V4) { - if (gfileRead(img->f, &priv->maskalpha, 4) != 4) goto baddatacleanup; - CONVERT_FROM_DWORD_LE(priv->maskalpha); - } else - priv->maskalpha = 0; - } else if (priv->bitsperpixel == 16) { - priv->bmpflags |= BMP_COMP_MASK; - priv->maskred = 0x7C00; - priv->maskgreen = 0x03E0; - priv->maskblue = 0x001F; - priv->maskalpha = 0; - } else if (priv->bitsperpixel == 32) { - priv->bmpflags |= BMP_COMP_MASK; - priv->maskred = 0x00FF0000; - priv->maskgreen = 0x0000FF00; - priv->maskblue = 0x000000FF; - priv->maskalpha = 0; - } - - /* We need to adjust the masks and calculate the shift values so the result scales 0 -> 255 */ - if (priv->bmpflags & BMP_COMP_MASK) { - priv->shiftred = 0; - priv->shiftgreen = 0; - priv->shiftblue = 0; - if (priv->maskred) { - if (priv->maskred < 256) - for(adword = priv->maskred; adword < 128; priv->shiftred--, adword <<= 1); - else - for(adword = priv->maskred; adword > 255; priv->shiftred++, adword >>= 1); - } - if (priv->maskgreen) { - if (priv->maskgreen < 256) - for(adword = priv->maskgreen; adword < 128; priv->shiftgreen--, adword <<= 1); - else - for(adword = priv->maskgreen; adword > 255; priv->shiftgreen++, adword >>= 1); - } - if (priv->maskblue) { - if (priv->maskblue < 256) - for(adword = priv->maskblue; adword < 128; priv->shiftblue--, adword <<= 1); - else - for(adword = priv->maskblue; adword > 255; priv->shiftblue++, adword >>= 1); - } - if (priv->maskalpha) { - if (priv->maskalpha < 256) - for(adword = priv->maskalpha; adword < 128; priv->shiftalpha--, adword <<= 1); - else - for(adword = priv->maskalpha; adword > 255; priv->shiftalpha++, adword >>= 1); - } - } -#endif - - img->type = GDISP_IMAGE_TYPE_BMP; - return GDISP_IMAGE_ERR_OK; - -baddatacleanup: - gdispImageClose_BMP(img); // Clean up the private data area - return GDISP_IMAGE_ERR_BADDATA; // Oops - something wrong - -unsupportedcleanup: - gdispImageClose_BMP(img); // Clean up the private data area - return GDISP_IMAGE_ERR_UNSUPPORTED; // Not supported -} - -static coord_t getPixels(gdispImage *img, coord_t x) { - gdispImagePrivate * priv; - color_t * pc; - coord_t len; - - priv = img->priv; - pc = priv->buf; - len = 0; - - switch(priv->bitsperpixel) { -#if GDISP_NEED_IMAGE_BMP_1 - case 1: - { - uint8_t b[4]; - uint8_t m; - - priv = img->priv; - pc = priv->buf; - len = 0; - - while(x < img->width && len <= BLIT_BUFFER_SIZE-32) { - if (gfileRead(img->f, &b, 4) != 4) - return 0; - - for(m=0x80; m; m >>= 1, pc++) - pc[0] = priv->palette[(m&b[0]) ? 1 : 0]; - for(m=0x80; m; m >>= 1, pc++) - pc[0] = priv->palette[(m&b[1]) ? 1 : 0]; - for(m=0x80; m; m >>= 1, pc++) - pc[0] = priv->palette[(m&b[2]) ? 1 : 0]; - for(m=0x80; m; m >>= 1, pc++) - pc[0] = priv->palette[(m&b[3]) ? 1 : 0]; - len += 32; - x += 32; - } - } - return len; -#endif - -#if GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE - case 4: - #if GDISP_NEED_IMAGE_BMP_4_RLE - #if GDISP_NEED_IMAGE_BMP_4 - if (priv->bmpflags & BMP_COMP_RLE) - #endif - { - uint8_t b[4]; - - while(x < img->width) { - if (priv->bmpflags & BMP_RLE_ENC) { - while (priv->rlerun && len <= BLIT_BUFFER_SIZE-2 && x < img->width) { - *pc++ = priv->palette[priv->rlecode >> 4]; - priv->rlerun--; - len++; - x++; - if (priv->rlerun) { - *pc++ = priv->palette[priv->rlecode & 0x0F]; - priv->rlerun--; - len++; - x++; - } - } - if (priv->rlerun) // Return if we have more run to do - return len; - } else if (priv->bmpflags & BMP_RLE_ABS) { - while (priv->rlerun && len <= BLIT_BUFFER_SIZE-2 && x < img->width) { - if (gfileRead(img->f, &b, 1) != 1) - return 0; - *pc++ = priv->palette[b[0] >> 4]; - priv->rlerun--; - len++; - x++; - if (priv->rlerun) { - *pc++ = priv->palette[b[0] & 0x0F]; - priv->rlerun--; - len++; - x++; - } - } - if (priv->rlerun) // Return if we have more run to do - return len; - if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary - if (gfileRead(img->f, &b, 1) != 1) - return 0; - } - } - - // We have finished the current run - read a new run - priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS); - - // There are always at least 2 bytes in an RLE code - if (gfileRead(img->f, &b, 2) != 2) - return 0; - - if (b[0]) { // Encoded mode - priv->rlerun = b[0]; - priv->rlecode = b[1]; - priv->bmpflags |= BMP_RLE_ENC; - } else if (b[1] == 0) { // End of line - if (x < img->width) { - priv->rlerun = img->width - x; - priv->rlecode = 0; // Who knows what color this should really be - priv->bmpflags |= BMP_RLE_ENC; - } - } else if (b[1] == 1) { // End of file - return len; - } else if (b[1] == 2) { // Delta x, y - // There are always at least 2 bytes in an RLE code - if (gfileRead(img->f, &b, 2) != 2) - return 0; - priv->rlerun = b[0] + (uint16_t)b[1] * img->width; - priv->rlecode = 0; // Who knows what color this should really be - priv->bmpflags |= BMP_RLE_ENC; - } else { // Absolute mode - priv->rlerun = b[1]; - priv->bmpflags |= BMP_RLE_ABS; - } - } - return len; - } - #endif - #if GDISP_NEED_IMAGE_BMP_4 - { - uint8_t b[4]; - - while(x < img->width && len <= BLIT_BUFFER_SIZE-8) { - if (gfileRead(img->f, &b, 4) != 4) - return 0; - - *pc++ = priv->palette[b[0] >> 4]; - *pc++ = priv->palette[b[0] & 0x0F]; - *pc++ = priv->palette[b[1] >> 4]; - *pc++ = priv->palette[b[1] & 0x0F]; - *pc++ = priv->palette[b[2] >> 4]; - *pc++ = priv->palette[b[2] & 0x0F]; - *pc++ = priv->palette[b[3] >> 4]; - *pc++ = priv->palette[b[3] & 0x0F]; - len += 8; - x += 8; - } - return len; - } - #endif -#endif - -#if GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - case 8: - #if GDISP_NEED_IMAGE_BMP_8_RLE - #if GDISP_NEED_IMAGE_BMP_8 - if (priv->bmpflags & BMP_COMP_RLE) - #endif - { - uint8_t b[4]; - - while(x < img->width) { - if (priv->bmpflags & BMP_RLE_ENC) { - while (priv->rlerun && len < BLIT_BUFFER_SIZE && x < img->width) { - *pc++ = priv->palette[priv->rlecode]; - priv->rlerun--; - len++; - x++; - } - if (priv->rlerun) // Return if we have more run to do - return len; - } else if (priv->bmpflags & BMP_RLE_ABS) { - while (priv->rlerun && len < BLIT_BUFFER_SIZE && x < img->width) { - if (gfileRead(img->f, &b, 1) != 1) - return 0; - *pc++ = priv->palette[b[0]]; - priv->rlerun--; - len++; - x++; - } - if (priv->rlerun) // Return if we have more run to do - return len; - if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary - if (gfileRead(img->f, &b, 1) != 1) - return 0; - } - } - - // We have finished the current run - read a new run - priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS); - - // There are always at least 2 bytes in an RLE code - if (gfileRead(img->f, &b, 2) != 2) - return 0; - - if (b[0]) { // Encoded mode - priv->rlerun = b[0]; - priv->rlecode = b[1]; - priv->bmpflags |= BMP_RLE_ENC; - } else if (b[1] == 0) { // End of line - if (x < img->width) { - priv->rlerun = img->width - x; - priv->rlecode = 0; // Who knows what color this should really be - priv->bmpflags |= BMP_RLE_ENC; - } - } else if (b[1] == 1) { // End of file - return len; - } else if (b[1] == 2) { // Delta x, y - // There are always at least 2 bytes in an RLE code - if (gfileRead(img->f, &b, 2) != 2) - return GDISP_IMAGE_ERR_BADDATA; - priv->rlerun = b[0] + (uint16_t)b[1] * img->width; - priv->rlecode = 0; // Who knows what color this should really be - priv->bmpflags |= BMP_RLE_ENC; - } else { // Absolute mode - priv->rlerun = b[1]; - priv->bmpflags |= BMP_RLE_ABS; - } - } - return len; - } - #endif - #if GDISP_NEED_IMAGE_BMP_8 - { - uint8_t b[4]; - - while(x < img->width && len <= BLIT_BUFFER_SIZE-4) { - if (gfileRead(img->f, &b, 4) != 4) - return 0; - - *pc++ = priv->palette[b[0]]; - *pc++ = priv->palette[b[1]]; - *pc++ = priv->palette[b[2]]; - *pc++ = priv->palette[b[3]]; - len += 4; - x += 4; - } - return len; - } - #endif -#endif - -#if GDISP_NEED_IMAGE_BMP_16 - case 16: - { - uint16_t w[2]; - color_t r, g, b; - - while(x < img->width && len <= BLIT_BUFFER_SIZE-2) { - if (gfileRead(img->f, &w, 4) != 4) - return 0; - CONVERT_FROM_WORD_LE(w[0]); - CONVERT_FROM_WORD_LE(w[1]); - if (priv->shiftred < 0) - r = (color_t)((w[0] & priv->maskred) << -priv->shiftred); - else - r = (color_t)((w[0] & priv->maskred) >> priv->shiftred); - if (priv->shiftgreen < 0) - g = (color_t)((w[0] & priv->maskgreen) << -priv->shiftgreen); - else - g = (color_t)((w[0] & priv->maskgreen) >> priv->shiftgreen); - if (priv->shiftblue < 0) - b = (color_t)((w[0] & priv->maskblue) << -priv->shiftblue); - else - b = (color_t)((w[0] & priv->maskblue) >> priv->shiftblue); - /* We don't support alpha yet */ - *pc++ = RGB2COLOR(r, g, b); - if (priv->shiftred < 0) - r = (color_t)((w[1] & priv->maskred) << -priv->shiftred); - else - r = (color_t)((w[1] & priv->maskred) >> priv->shiftred); - if (priv->shiftgreen < 0) - g = (color_t)((w[1] & priv->maskgreen) << -priv->shiftgreen); - else - g = (color_t)((w[1] & priv->maskgreen) >> priv->shiftgreen); - if (priv->shiftblue < 0) - b = (color_t)((w[1] & priv->maskblue) << -priv->shiftblue); - else - b = (uint8_t)((w[1] & priv->maskblue) >> priv->shiftblue); - /* We don't support alpha yet */ - *pc++ = RGB2COLOR(r, g, b); - x += 2; - len += 2; - } - } - return len; -#endif - -#if GDISP_NEED_IMAGE_BMP_24 - case 24: - { - uint8_t b[3]; - - while(x < img->width && len < BLIT_BUFFER_SIZE) { - if (gfileRead(img->f, &b, 3) != 3) - return 0; - *pc++ = RGB2COLOR(b[2], b[1], b[0]); - x++; - len++; - } - - if (x >= img->width) { - // Make sure we have read a multiple of 4 bytes for the line - if ((x & 3) && gfileRead(img->f, &b, x & 3) != (x & 3)) - return 0; - } - } - return len; -#endif - -#if GDISP_NEED_IMAGE_BMP_32 - case 32: - { - uint32_t dw; - color_t r, g, b; - - while(x < img->width && len < BLIT_BUFFER_SIZE) { - if (gfileRead(img->f, &dw, 4) != 4) - return 0; - CONVERT_FROM_DWORD_LE(dw); - if (priv->shiftred < 0) - r = (color_t)((dw & priv->maskred) << -priv->shiftred); - else - r = (color_t)((dw & priv->maskred) >> priv->shiftred); - if (priv->shiftgreen < 0) - g = (color_t)((dw & priv->maskgreen) << -priv->shiftgreen); - else - g = (color_t)((dw & priv->maskgreen) >> priv->shiftgreen); - if (priv->shiftblue < 0) - b = (color_t)((dw & priv->maskblue) << -priv->shiftblue); - else - b = (color_t)((dw & priv->maskblue) >> priv->shiftblue); - /* We don't support alpha yet */ - *pc++ = RGB2COLOR(r, g, b); - x++; - len++; - } - } - return len; -#endif - - default: - return len; - } -} - -gdispImageError gdispImageCache_BMP(gdispImage *img) { - gdispImagePrivate * priv; - color_t * pcs; - color_t * pcd; - coord_t pos, x, y; - size_t len; - - /* If we are already cached - just return OK */ - priv = img->priv; - if (priv->frame0cache) - return GDISP_IMAGE_ERR_OK; - - /* We need to allocate the cache */ - len = img->width * img->height * sizeof(pixel_t); - priv->frame0cache = (pixel_t *)gdispImageAlloc(img, len); - if (!priv->frame0cache) - return GDISP_IMAGE_ERR_NOMEMORY; - - /* Read the entire bitmap into cache */ - gfileSetPos(img->f, priv->frame0pos); -#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE - priv->rlerun = 0; - priv->rlecode = 0; -#endif - - if (priv->bmpflags & BMP_TOP_TO_BOTTOM) { - for(y = 0, pcd = priv->frame0cache; y < img->height; y++) { - x = 0; pos = 0; - while(x < img->width) { - if (!pos) { - if (!(pos = getPixels(img, x))) - return GDISP_IMAGE_ERR_BADDATA; - pcs = priv->buf; - } - *pcd++ = *pcs++; - x++; pos--; - } - } - } else { - for(y = img->height-1, pcd = priv->frame0cache + img->width*(img->height-1); y >= 0; y--, pcd -= 2*img->width) { - x = 0; pos = 0; - while(x < img->width) { - if (!pos) { - if (!(pos = getPixels(img, x))) - return GDISP_IMAGE_ERR_BADDATA; - pcs = priv->buf; - } - *pcd++ = *pcs++; - x++; pos--; - } - } - } - - return GDISP_IMAGE_ERR_OK; -} - -gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { - gdispImagePrivate * priv; - coord_t mx, my; - coord_t pos, len, st; - - priv = img->priv; - - /* Check some reasonableness */ - if (sx >= img->width || sy >= img->height) return GDISP_IMAGE_ERR_OK; - if (sx + cx > img->width) cx = img->width - sx; - if (sy + cy > img->height) cy = img->height - sy; - - /* Draw from the image cache - if it exists */ - if (priv->frame0cache) { - gdispGBlitArea(g, x, y, cx, cy, sx, sy, img->width, priv->frame0cache); - return GDISP_IMAGE_ERR_OK; - } - - /* Start decoding from the beginning */ - gfileSetPos(img->f, priv->frame0pos); -#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE - priv->rlerun = 0; - priv->rlecode = 0; -#endif - - if (priv->bmpflags & BMP_TOP_TO_BOTTOM) { - for(my = 0; my < img->height; my++) { - mx = 0; - while(mx < img->width) { - if (!(pos = getPixels(img, mx))) - return GDISP_IMAGE_ERR_BADDATA; - if (my >= sy && my < sy+cy && mx < sx+cx && mx+pos >= sx) { - st = mx < sx ? sx - mx : 0; - len = pos-st; - if (mx+st+len > sx+cx) len = sx+cx-mx-st; - if (len == 1) - gdispGDrawPixel(g, x+mx+st-sx, y+my-sy, priv->buf[st]); - else - gdispGBlitArea(g, x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf); - } - mx += pos; - } - } - } else { - for(my = img->height-1; my >= 0; my--) { - mx = 0; - while(mx < img->width) { - if (!(pos = getPixels(img, mx))) - return GDISP_IMAGE_ERR_BADDATA; - if (my >= sy && my < sy+cy && mx < sx+cx && mx+pos >= sx) { - st = mx < sx ? sx - mx : 0; - len = pos-st; - if (mx+st+len > sx+cx) len = sx+cx-mx-st; - if (len == 1) - gdispGDrawPixel(g, x+mx+st-sx, y+my-sy, priv->buf[st]); - else - gdispGBlitArea(g, x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf); - } - mx += pos; - } - } - } - - return GDISP_IMAGE_ERR_OK; -} - -delaytime_t gdispImageNext_BMP(gdispImage *img) { - (void) img; - - /* No more frames/pages */ - return TIME_INFINITE; -} - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_BMP */ -/** @} */ diff --git a/src/gdisp/image_gif.c b/src/gdisp/image_gif.c deleted file mode 100644 index 06f4ef6a..00000000 --- a/src/gdisp/image_gif.c +++ /dev/null @@ -1,1208 +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/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 - -/** - * Helper Routines Needed - */ -void *gdispImageAlloc(gdispImage *img, size_t sz); -void gdispImageFree(gdispImage *img, void *ptr, size_t sz); - -/** - * How big an array to allocate for blitting (in pixels) - * Bigger is faster but uses more RAM. - */ -#define BLIT_BUFFER_SIZE 32 - -/* - * Determining endianness as at compile time is not guaranteed or compiler portable. - * We use the best test we can. If we can't guarantee little endianness we do things the - * hard way. - */ -#define GUARANTEED_LITTLE_ENDIAN (!defined(SAFE_ENDIAN) && !defined(SAFE_ALIGNMENT) && (\ - (defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \ - || defined(__LITTLE_ENDIAN__) \ - || defined(__LITTLE_ENDIAN) \ - || defined(_LITTLE_ENDIAN) \ -/* || (1 == *(unsigned char *)&(const int){1})*/ \ - )) - - -/* This is a runtime test */ -static const uint8_t dwordOrder[4] = { 1, 2, 3, 4 }; - -#define isWordLittleEndian() (*(uint16_t *)&dwordOrder == 0x0201) -#define isDWordLittleEndian() (*(uint32_t *)&dwordOrder == 0x04030201) - -#if GUARANTEED_LITTLE_ENDIAN - /* These are fast routines for guaranteed little endian machines */ - #define CONVERT_FROM_WORD_LE(w) - #define CONVERT_FROM_DWORD_LE(dw) -#else - /* These are slower routines for when little endianness cannot be guaranteed at compile time */ - #define CONVERT_FROM_WORD_LE(w) { if (!isWordLittleEndian()) w = ((((uint16_t)(w))>>8)|(((uint16_t)(w))<<8)); } - #define CONVERT_FROM_DWORD_LE(dw) { if (!isDWordLittleEndian()) dw = (((uint32_t)(((const uint8_t *)(&dw))[0]))|(((uint32_t)(((const uint8_t *)(&dw))[1]))<<8)|(((uint32_t)(((const uint8_t *)(&dw))[2]))<<16)|(((uint32_t)(((const uint8_t *)(&dw))[3]))<<24)); } -#endif - -// We need a special error to indicate the end of file (which may not actually be an error) -#define GDISP_IMAGE_EOF ((gdispImageError)-1) -#define GDISP_IMAGE_LOOP ((gdispImageError)-2) - -#define MAX_CODE_BITS 12 -#define CODE_MAX ((1<priv; - - // We need the decode ram, and possibly a palette - if (!(decode = (imgdecode *)gdispImageAlloc(img, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)))) - return GDISP_IMAGE_ERR_NOMEMORY; - - // We currently have not read any image data block - decode->blocksz = 0; - - // Set the palette - if (priv->frame.palsize) { - // Local palette - decode->maxpixel = priv->frame.palsize-1; - decode->palette = (color_t *)(decode+1); - gfileSetPos(img->f, priv->frame.pospal); - for(cnt = 0; cnt < priv->frame.palsize; cnt++) { - if (gfileRead(img->f, &decode->buf, 3) != 3) - goto baddatacleanup; - decode->palette[cnt] = RGB2COLOR(decode->buf[0], decode->buf[1], decode->buf[2]); - } - } else if (priv->palette) { - // Global palette - decode->maxpixel = priv->palsize-1; - decode->palette = priv->palette; - } else { - // Oops - we must have a palette - goto baddatacleanup; - } - - // Get the initial lzw code size and values - gfileSetPos(img->f, priv->frame.posimg); - if (gfileRead(img->f, &decode->bitsperpixel, 1) != 1 || decode->bitsperpixel >= MAX_CODE_BITS) - goto baddatacleanup; - decode->code_clear = 1 << decode->bitsperpixel; - decode->code_eof = decode->code_clear + 1; - decode->code_max = decode->code_clear + 2; - decode->code_last = CODE_NONE; - decode->bitspercode = decode->bitsperpixel+1; - decode->maxcodesz = 1 << decode->bitspercode; - decode->shiftbits = 0; - decode->shiftdata = 0; - decode->stackcnt = 0; - for(cnt = 0; cnt <= CODE_MAX; cnt++) - decode->prefix[cnt] = CODE_NONE; - - // All ready to go - priv->decode = decode; - return GDISP_IMAGE_ERR_OK; - -baddatacleanup: - gdispImageFree(img, decode, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)); - return GDISP_IMAGE_ERR_BADDATA; -} - -/** - * Stop decoding a frame. - * - * Pre: Frame info has been read. - */ -static void stopDecode(gdispImage *img) { - gdispImagePrivate * priv; - - priv = img->priv; - - // Free the decode data - if (priv->decode) { - gdispImageFree(img, (void *)priv->decode, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)); - priv->decode = 0; - } -} - -static uint16_t getPrefix(imgdecode *decode, uint16_t code) { - uint16_t i; - - for(i=0; code > decode->code_clear && i <= CODE_MAX; i++, code = decode->prefix[code]) { - if (code > CODE_MAX) - return CODE_NONE; - } - return code; -} - -/** - * Decode some pixels from a frame. - * - * Pre: We are ready for decoding. - * - * Return: The number of pixels decoded 0 .. BLIT_BUFFER_SIZE-1. 0 means EOF - * - * Note: The resulting pixels are stored in decode->buf - */ -static uint16_t getbytes(gdispImage *img) { - gdispImagePrivate * priv; - imgdecode * decode; - uint16_t cnt; - uint16_t code, prefix; - uint8_t bdata; - - priv = img->priv; - decode = priv->decode; - cnt = 0; - - // At EOF - if (decode->code_last == decode->code_eof) - return 0; - - while(cnt < sizeof(decode->buf)) { - // Use the stack up first - if (decode->stackcnt > 0) { - decode->buf[cnt++] = decode->stack[--decode->stackcnt]; - continue; - } - - // Get another code - a code is made up of decode->bitspercode bits. - while (decode->shiftbits < decode->bitspercode) { - // Get a byte - we may have to start a new data block - if ((!decode->blocksz && (gfileRead(img->f, &decode->blocksz, 1) != 1 || !decode->blocksz)) - || gfileRead(img->f, &bdata, 1) != 1) { - // Pretend we got the EOF code - some encoders seem to just end the file - decode->code_last = decode->code_eof; - return cnt; - } - decode->blocksz--; - - decode->shiftdata |= ((unsigned long)bdata) << decode->shiftbits; - decode->shiftbits += 8; - } - code = decode->shiftdata & BitMask[decode->bitspercode]; - decode->shiftdata >>= decode->bitspercode; - decode->shiftbits -= decode->bitspercode; - /** - * If code cannot fit into bitspercode bits we must raise its size. - * Note that codes above CODE_MAX are used for special signaling. - * If we're using MAX_CODE_BITS bits already and we're at the max code, just - * keep using the table as it is, don't increment decode->bitspercode. - */ - if (decode->code_max < CODE_MAX + 2 && ++decode->code_max > decode->maxcodesz && decode->bitspercode < MAX_CODE_BITS) { - decode->maxcodesz <<= 1; - decode->bitspercode++; - } - - // EOF - the appropriate way to stop decoding - if (code == decode->code_eof) { - // Skip to the end of the data blocks - do { - gfileSetPos(img->f, gfileGetPos(img->f)+decode->blocksz); - } while (gfileRead(img->f, &decode->blocksz, 1) == 1 && decode->blocksz); - - // Mark the end - decode->code_last = decode->code_eof; - break; - } - - if (code == decode->code_clear) { - // Start again - for(prefix = 0; prefix <= CODE_MAX; prefix++) - decode->prefix[prefix] = CODE_NONE; - decode->code_max = decode->code_eof + 1; - decode->bitspercode = decode->bitsperpixel + 1; - decode->maxcodesz = 1 << decode->bitspercode; - decode->code_last = CODE_NONE; - continue; - } - - if (code < decode->code_clear) { - // Simple unencoded pixel - add it - decode->buf[cnt++] = code; - - } else { - /** - * Its a LZW code - trace the linked list until the prefix is a - * valid pixel while pushing the suffix pixels on the stack. - * If done, pop the stack in reverse order adding the pixels - */ - if (decode->prefix[code] != CODE_NONE) - prefix = code; - - /** - * Only allowed if the code equals the partial code. - * In that case code = XXXCode, CrntCode or the - * prefix code is last code and the suffix char is - * exactly the prefix of last code! - */ - else if (code == decode->code_max - 2 && decode->stackcnt < sizeof(decode->stack)) { - prefix = decode->code_last; - decode->suffix[decode->code_max - 2] = decode->stack[decode->stackcnt++] = getPrefix(decode, decode->code_last); - } else - return 0; - - /** - * If the image is OK we should not get a CODE_NONE while tracing. - * To prevent looping with a bad image we use StackPtr as loop counter - * and stop before overflowing Stack[]. - */ - while (decode->stackcnt < sizeof(decode->stack) && prefix > decode->code_clear && prefix <= CODE_MAX) { - decode->stack[decode->stackcnt++] = decode->suffix[prefix]; - prefix = decode->prefix[prefix]; - } - if (decode->stackcnt >= sizeof(decode->stack) || prefix > CODE_MAX) - return 0; - - /* Push the last character on stack: */ - decode->stack[decode->stackcnt++] = prefix; - } - - if (decode->code_last != CODE_NONE && decode->prefix[decode->code_max - 2] == CODE_NONE) { - decode->prefix[decode->code_max - 2] = decode->code_last; - - /* Only allowed if code is exactly the running code: - * In that case code = XXXCode, CrntCode or the - * prefix code is last code and the suffix char is - * exactly the prefix of last code! */ - decode->suffix[decode->code_max - 2] = getPrefix(decode, code == decode->code_max - 2 ? decode->code_last : code); - } - decode->code_last = code; - } - return cnt; -} - -/** - * Read the info on a frame. - * - * Pre: The file position is at the start of the frame. - */ -static gdispImageError initFrame(gdispImage *img) { - gdispImagePrivate * priv; - imgcache * cache; - uint8_t blocktype; - uint8_t blocksz; - - priv = img->priv; - - // Save the dispose info from the existing frame - priv->dispose.flags = priv->frame.flags; - priv->dispose.paltrans = priv->frame.paltrans; - priv->dispose.x = priv->frame.x; - priv->dispose.y = priv->frame.y; - priv->dispose.width = priv->frame.width; - priv->dispose.height = priv->frame.height; - - // Check for a cached version of this image - for(cache=priv->cache; cache && cache->frame.posstart <= (size_t)gfileGetPos(img->f); cache=cache->next) { - if (cache->frame.posstart == (size_t)gfileGetPos(img->f)) { - priv->frame = cache->frame; - priv->curcache = cache; - return GDISP_IMAGE_ERR_OK; - } - } - - // Get ready for a new image - priv->curcache = 0; - priv->frame.posstart = gfileGetPos(img->f); - priv->frame.flags = 0; - priv->frame.delay = 0; - priv->frame.palsize = 0; - - // Process blocks until we reach the image descriptor - while(1) { - if (gfileRead(img->f, &blocktype, 1) != 1) - return GDISP_IMAGE_ERR_BADDATA; - - switch(blocktype) { - case 0x2C: //',' - IMAGE_DESC_RECORD_TYPE; - // Read the Image Descriptor - if (gfileRead(img->f, priv->buf, 9) != 9) - return GDISP_IMAGE_ERR_BADDATA; - priv->frame.x = *(uint16_t *)(((uint8_t *)priv->buf)+0); - CONVERT_FROM_WORD_LE(priv->frame.x); - priv->frame.y = *(uint16_t *)(((uint8_t *)priv->buf)+2); - CONVERT_FROM_WORD_LE(priv->frame.y); - priv->frame.width = *(uint16_t *)(((uint8_t *)priv->buf)+4); - CONVERT_FROM_WORD_LE(priv->frame.width); - priv->frame.height = *(uint16_t *)(((uint8_t *)priv->buf)+6); - CONVERT_FROM_WORD_LE(priv->frame.height); - if (((uint8_t *)priv->buf)[8] & 0x80) // Local color table? - priv->frame.palsize = 2 << (((uint8_t *)priv->buf)[8] & 0x07); - if (((uint8_t *)priv->buf)[8] & 0x40) // Interlaced? - priv->frame.flags |= GIFL_INTERLACE; - - // We are ready to go for the actual palette read and image decode - priv->frame.pospal = gfileGetPos(img->f); - priv->frame.posimg = priv->frame.pospal+priv->frame.palsize*3; - priv->frame.posend = 0; - - // Mark this as an animated image if more than 1 frame. - if (priv->frame.posstart != priv->frame0pos) - img->flags |= GDISP_IMAGE_FLG_ANIMATED; - return GDISP_IMAGE_ERR_OK; - - case 0x21: //'!' - EXTENSION_RECORD_TYPE; - // Read the extension type - if (gfileRead(img->f, &blocktype, 1) != 1) - return GDISP_IMAGE_ERR_BADDATA; - - switch(blocktype) { - case 0xF9: // EXTENSION - Graphics Control Block - // Read the GCB - if (gfileRead(img->f, priv->buf, 6) != 6) - return GDISP_IMAGE_ERR_BADDATA; - // Check we have read a 4 byte data block and a data block terminator (0) - if (((uint8_t *)priv->buf)[0] != 4 || ((uint8_t *)priv->buf)[5] != 0) - return GDISP_IMAGE_ERR_BADDATA; - // Process the flags - switch(((uint8_t *)priv->buf)[1] & 0x1C) { - case 0x00: case 0x04: break; // Dispose = do nothing - case 0x08: priv->frame.flags |= GIFL_DISPOSECLEAR; break; // Dispose = clear - case 0x0C: case 0x10: priv->frame.flags |= GIFL_DISPOSEREST; break; // Dispose = restore. Value 0x10 is a hack for bad encoders - default: return GDISP_IMAGE_ERR_UNSUPPORTED; - } - if (((uint8_t *)priv->buf)[1] & 0x01) { - priv->frame.flags |= GIFL_TRANSPARENT; - img->flags |= GDISP_IMAGE_FLG_TRANSPARENT; // We set this but never clear it - } - if (((uint8_t *)priv->buf)[1] & 0x02) // Wait for user input? - img->flags |= GDISP_IMAGE_FLG_MULTIPAGE; - else - img->flags &= ~GDISP_IMAGE_FLG_MULTIPAGE; - // Process frame delay and the transparent color (if any) - priv->frame.delay = *(uint16_t *)(((uint8_t *)priv->buf)+2); - CONVERT_FROM_WORD_LE(priv->frame.delay); - priv->frame.paltrans = ((uint8_t *)priv->buf)[4]; - break; - - case 0xFF: // EXTENSION - Application - // We only handle this for the special Netscape loop counter for animation - if (priv->flags & GIF_LOOP) - goto skipdatablocks; - // Read the Application header - if (gfileRead(img->f, priv->buf, 16) != 16) - return GDISP_IMAGE_ERR_BADDATA; - // Check we have read a 11 byte data block - if (((uint8_t *)priv->buf)[0] != 11 && ((uint8_t *)priv->buf)[12] != 3) - return GDISP_IMAGE_ERR_BADDATA; - // Check the vendor - if (((uint8_t *)priv->buf)[1] == 'N' && ((uint8_t *)priv->buf)[2] == 'E' && ((uint8_t *)priv->buf)[3] == 'T' - && ((uint8_t *)priv->buf)[4] == 'S' && ((uint8_t *)priv->buf)[5] == 'C' && ((uint8_t *)priv->buf)[6] == 'A' - && ((uint8_t *)priv->buf)[7] == 'P' && ((uint8_t *)priv->buf)[8] == 'E' && ((uint8_t *)priv->buf)[9] == '2' - && ((uint8_t *)priv->buf)[10] == '.' && ((uint8_t *)priv->buf)[11] == '0') { - if (((uint8_t *)priv->buf)[13] == 1) { - priv->loops = *(uint16_t *)(((uint8_t *)priv->buf)+14); - CONVERT_FROM_WORD_LE(priv->loops); - priv->flags |= GIF_LOOP; - if (!priv->loops) - priv->flags |= GIF_LOOPFOREVER; - } - } - goto skipdatablocks; - - case 0x01: // EXTENSION - Plain Text (Graphics Rendering) - case 0xFE: // EXTENSION - Comment - default: - // 0x00-0x7F (0-127) are the Graphic Rendering blocks - if (blocktype <= 0x7F) - return GDISP_IMAGE_ERR_UNSUPPORTED; - // 0x80-0xF9 (128-249) are the Control blocks - // 0xFA-0xFF (250-255) are the Special Purpose blocks - // We don't understand this extension - just skip it by skipping data blocks - skipdatablocks: - while(1) { - if (gfileRead(img->f, &blocksz, 1) != 1) - return GDISP_IMAGE_ERR_BADDATA; - if (!blocksz) - break; - gfileSetPos(img->f, gfileGetPos(img->f) + blocksz); - } - break; - } - break; - - case 0x3B: //';' - TERMINATE_RECORD_TYPE; - // Are we an looping animation - if (!(priv->flags & GIF_LOOP)) - return GDISP_IMAGE_EOF; - if (!(priv->flags & GIF_LOOPFOREVER)) { - if (!priv->loops) - return GDISP_IMAGE_EOF; - priv->loops--; - } - - // Seek back to frame0 - gfileSetPos(img->f, priv->frame0pos); - return GDISP_IMAGE_LOOP; - - default: // UNDEFINED_RECORD_TYPE; - return GDISP_IMAGE_ERR_UNSUPPORTED; - } - } -} - -void gdispImageClose_GIF(gdispImage *img) { - gdispImagePrivate * priv; - imgcache * cache; - imgcache * ncache; - - priv = img->priv; - if (priv) { - // Free any stored frames - cache = priv->cache; - while(cache) { - ncache = cache->next; - gdispImageFree(img, (void *)cache, sizeof(imgcache)+cache->frame.width*cache->frame.height+cache->frame.palsize*sizeof(color_t)); - cache = ncache; - } - if (priv->palette) - gdispImageFree(img, (void *)priv->palette, priv->palsize*sizeof(color_t)); - gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); - img->priv = 0; - } -} - -gdispImageError gdispImageOpen_GIF(gdispImage *img) { - gdispImagePrivate *priv; - uint8_t hdr[6]; - uint16_t aword; - - /* Read the file identifier */ - if (gfileRead(img->f, hdr, 6) != 6) - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - /* Process the GIFFILEHEADER structure */ - - if (hdr[0] != 'G' || hdr[1] != 'I' || hdr[2] != 'F' - || hdr[3] != '8' || (hdr[4] != '7' && hdr[4] != '9') || hdr[5] != 'a') - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - /* We know we are a GIF format image */ - img->flags = 0; - - /* Allocate our private area */ - if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) - return GDISP_IMAGE_ERR_NOMEMORY; - - /* Initialise the essential bits in the private area */ - priv = img->priv; - priv->flags = 0; - priv->palsize = 0; - priv->palette = 0; - priv->frame.flags = 0; - priv->cache = 0; - priv->curcache = 0; - - /* Process the Screen Descriptor structure */ - - // Read the screen descriptor - if (gfileRead(img->f, priv->buf, 7) != 7) - goto baddatacleanup; - // Get the width - img->width = *(uint16_t *)(((uint8_t *)priv->buf)+0); - CONVERT_FROM_WORD_LE(img->width); - // Get the height - img->height = *(uint16_t *)(((uint8_t *)priv->buf)+2); - CONVERT_FROM_WORD_LE(img->height); - if (((uint8_t *)priv->buf)[4] & 0x80) { - // Global color table - priv->palsize = 2 << (((uint8_t *)priv->buf)[4] & 0x07); - // Allocate the global palette - if (!(priv->palette = (color_t *)gdispImageAlloc(img, priv->palsize*sizeof(color_t)))) - goto nomemcleanup; - // Read the global palette - for(aword = 0; aword < priv->palsize; aword++) { - if (gfileRead(img->f, &priv->buf, 3) != 3) - goto baddatacleanup; - priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[0], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[2]); - } - } - priv->bgcolor = ((uint8_t *)priv->buf)[5]; - - // Save the fram0pos - priv->frame0pos = gfileGetPos(img->f); - - // Read the first frame descriptor - switch(initFrame(img)) { - case GDISP_IMAGE_ERR_OK: // Everything OK - img->type = GDISP_IMAGE_TYPE_GIF; - return GDISP_IMAGE_ERR_OK; - case GDISP_IMAGE_ERR_UNSUPPORTED: // Unsupported - gdispImageClose_GIF(img); // Clean up the private data area - return GDISP_IMAGE_ERR_UNSUPPORTED; - case GDISP_IMAGE_ERR_NOMEMORY: // Out of Memory - nomemcleanup: - gdispImageClose_GIF(img); // Clean up the private data area - return GDISP_IMAGE_ERR_NOMEMORY; - case GDISP_IMAGE_EOF: // We should have a frame but we don't seem to - case GDISP_IMAGE_LOOP: // We should have a frame but we don't seem to - case GDISP_IMAGE_ERR_BADDATA: // Oops - something wrong with the data - default: - baddatacleanup: - gdispImageClose_GIF(img); // Clean up the private data area - return GDISP_IMAGE_ERR_BADDATA; - } -} - -gdispImageError gdispImageCache_GIF(gdispImage *img) { - gdispImagePrivate * priv; - imgcache * cache; - imgdecode * decode; - uint8_t * p; - uint8_t * q; - coord_t mx, my; - uint16_t cnt; - - /* If we are already cached - just return OK */ - priv = img->priv; - if (priv->curcache) - return GDISP_IMAGE_ERR_OK; - - /* We need to allocate the frame, the palette and bits for the image */ - if (!(cache = (imgcache *)gdispImageAlloc(img, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height))) - return GDISP_IMAGE_ERR_NOMEMORY; - - /* Initialise the cache */ - decode = 0; - cache->frame = priv->frame; - cache->imagebits = (uint8_t *)(cache+1) + cache->frame.palsize*sizeof(color_t); - cache->next = 0; - - /* Start the decode */ - switch(startDecode(img)) { - case GDISP_IMAGE_ERR_OK: break; - case GDISP_IMAGE_ERR_NOMEMORY: goto nomemcleanup; - case GDISP_IMAGE_ERR_BADDATA: - default: goto baddatacleanup; - } - decode = priv->decode; - - // Save the palette - if (cache->frame.palsize) { - cache->palette = (color_t *)(cache+1); - - /* Copy the local palette into the cache */ - for(cnt = 0; cnt < cache->frame.palsize; cnt++) - cache->palette[cnt] = decode->palette[cnt]; - } else - cache->palette = priv->palette; - - // Check for interlacing - cnt = 0; - if (cache->frame.flags & GIFL_INTERLACE) { - // Every 8th row starting at row 0 - for(p=cache->imagebits, my=0; my < cache->frame.height; my+=8, p += cache->frame.width*7) { - for(mx=0; mx < cache->frame.width; mx++) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - while(cnt < sizeof(decode->buf)) - decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; - } - q = decode->buf; - } - *p++ = *q++; - cnt--; - } - } - // Every 8th row starting at row 4 - for(p=cache->imagebits+cache->frame.width*4, my=4; my < cache->frame.height; my+=8, p += cache->frame.width*7) { - for(mx=0; mx < cache->frame.width; mx++) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - while(cnt < sizeof(decode->buf)) - decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; - } - q = decode->buf; - } - *p++ = *q++; - cnt--; - } - } - // Every 4th row starting at row 2 - for(p=cache->imagebits+cache->frame.width*2, my=2; my < cache->frame.height; my+=4, p += cache->frame.width*3) { - for(mx=0; mx < cache->frame.width; mx++) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - while(cnt < sizeof(decode->buf)) - decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; - } - q = decode->buf; - } - *p++ = *q++; - cnt--; - } - } - // Every 2nd row starting at row 1 - for(p=cache->imagebits+cache->frame.width, my=1; my < cache->frame.height; my+=2, p += cache->frame.width) { - for(mx=0; mx < cache->frame.width; mx++) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - while(cnt < sizeof(decode->buf)) - decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; - } - q = decode->buf; - } - *p++ = *q++; - cnt--; - } - } - } else { - // Every row in sequence - p=cache->imagebits; - for(my=0; my < cache->frame.height; my++) { - for(mx=0; mx < cache->frame.width; mx++) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - while(cnt < sizeof(decode->buf)) - decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; - } - q = decode->buf; - } - *p++ = *q++; - cnt--; - } - } - } - // We could be pedantic here but extra bytes won't hurt us - while(getbytes(img)); - priv->frame.posend = cache->frame.posend = gfileGetPos(img->f); - - // Save everything - priv->curcache = cache; - if (!priv->cache) - priv->cache = cache; - else if (priv->cache->frame.posstart > cache->frame.posstart) { - cache->next = priv->cache; - priv->cache = cache; - } else { - imgcache *pc; - - for(pc = priv->cache; pc; pc = pc->next) { - if (!pc->next || pc->next->frame.posstart > cache->frame.posstart) { - cache->next = pc->next; - pc->next = cache; - break; - } - } - } - stopDecode(img); - return GDISP_IMAGE_ERR_OK; - -nomemcleanup: - stopDecode(img); - gdispImageFree(img, cache, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height); - return GDISP_IMAGE_ERR_NOMEMORY; - -baddatacleanup: - stopDecode(img); - gdispImageFree(img, cache, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height); - return GDISP_IMAGE_ERR_BADDATA; -} - -gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { - gdispImagePrivate * priv; - imgdecode * decode; - uint8_t * q = 0; - coord_t mx, my, fx, fy; - uint16_t cnt, gcnt; - uint8_t col; - - priv = img->priv; - - /* Handle previous frame disposing */ - if (priv->dispose.flags & (GIFL_DISPOSECLEAR|GIFL_DISPOSEREST)) { - // Clip to the disposal area - clip area = mx,my -> fx, fy (sx,sy,cx,cy are unchanged) - mx = priv->dispose.x; - my = priv->dispose.y; - fx = priv->dispose.x+priv->dispose.width; - fy = priv->dispose.y+priv->dispose.height; - if (sx > mx) mx = sx; - if (sy > my) my = sy; - if (sx+cx <= fx) fx = sx+cx; - if (sy+cy <= fy) fy = sy+cy; - if (fx > mx && fy > my) { - // We only support clearing (not restoring). The specification says that we are allowed to do this. - // Calculate the bgcolor - // The spec says to restore the backgound color (priv->bgcolor) but in practice if there is transparency - // image decoders tend to assume that a restore to the transparent color is required instead - if (((priv->dispose.flags & GIFL_TRANSPARENT) /*&& priv->dispose.paltrans == priv->bgcolor*/) || priv->bgcolor >= priv->palsize) - gdispGFillArea(g, x+mx-sx, y+my-sy, fx-mx, fy-my, img->bgcolor); - else - gdispGFillArea(g, x+mx-sx, y+my-sy, fx-mx, fy-my, priv->palette[priv->bgcolor]); - } - } - - /* Clip to just this frame - clip area = sx,sy -> fx, fy */ - fx = priv->frame.x+priv->frame.width; - fy = priv->frame.y+priv->frame.height; - if (sx >= fx || sy >= fy || sx+cx < priv->frame.x || sy+cy < priv->frame.y) return GDISP_IMAGE_ERR_OK; - if (sx < priv->frame.x) { mx = priv->frame.x - sx; x += mx; cx -= mx; sx = priv->frame.x; } - if (sy < priv->frame.y) { my = priv->frame.y - sy; y += my; cy -= my; sy = priv->frame.y; } - if (sx+cx > fx) cx = fx-sx; - if (sy+cy > fy) cy = fy-sy; - - // Make sx, sy relative to this frame so we are not adding priv->frame.x & priv->frame.y each time - sx -= priv->frame.x; sy -= priv->frame.y; - fx = sx + cx; - fy = sy + cy; - - /* Draw from the image cache - if it exists */ - if (priv->curcache) { - imgcache * cache; - - cache = priv->curcache; - q = cache->imagebits+priv->frame.width*sy+sx; - - for(my=sy; my < fy; my++, q += priv->frame.width - cx) { - for(gcnt=0, mx=sx, cnt=0; mx < fx; mx++) { - col = *q++; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = cache->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - - return GDISP_IMAGE_ERR_OK; - } - - /* Start the decode */ - switch(startDecode(img)) { - case GDISP_IMAGE_ERR_OK: break; - case GDISP_IMAGE_ERR_NOMEMORY: return GDISP_IMAGE_ERR_NOMEMORY; - case GDISP_IMAGE_ERR_BADDATA: - default: return GDISP_IMAGE_ERR_BADDATA; - } - decode = priv->decode; - - // Check for interlacing - cnt = 0; - if (priv->frame.flags & GIFL_INTERLACE) { - // Every 8th row starting at row 0 - for(my=0; my < priv->frame.height; my+=8) { - for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - mx++; - break; - } - q = decode->buf; - } - if (my >= sy && my < fy && mx >= sx && mx < fx) { - col = *q; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = decode->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - continue; - } - // We have finished the visible area - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - // Every 8th row starting at row 4 - for(my=4; my < priv->frame.height; my+=8) { - for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - mx++; - break; - } - q = decode->buf; - } - if (my >= sy && my < fy && mx >= sx && mx < fx) { - col = *q; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = decode->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - continue; - } - // We have finished the visible area - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - // Every 4th row starting at row 2 - for(my=2; my < priv->frame.height; my+=4) { - for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - mx++; - break; - } - q = decode->buf; - } - if (my >= sy && my < fy && mx >= sx && mx < fx) { - col = *q; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = decode->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - continue; - } - // We have finished the visible area - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - // Every 2nd row starting at row 1 - for(my=1; my < priv->frame.height; my+=2) { - for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - mx++; - break; - } - q = decode->buf; - } - if (my >= sy && my < fy && mx >= sx && mx < fx) { - col = *q; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = decode->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - continue; - } - // We have finished the visible area - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - } else { - // Every row in sequence - for(my=0; my < priv->frame.height; my++) { - for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - mx++; - break; - } - q = decode->buf; - } - if (my >= sy && my < fy && mx >= sx && mx < fx) { - col = *q; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = decode->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - continue; - } - // We have finished the visible area - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - } - // We could be pedantic here but extra bytes won't hurt us - while (getbytes(img)); - priv->frame.posend = gfileGetPos(img->f); - - stopDecode(img); - return GDISP_IMAGE_ERR_OK; - -baddatacleanup: - stopDecode(img); - return GDISP_IMAGE_ERR_BADDATA; -} - -delaytime_t gdispImageNext_GIF(gdispImage *img) { - gdispImagePrivate * priv; - delaytime_t delay; - uint8_t blocksz; - - priv = img->priv; - - // Save the delay and convert to millisecs - delay = (delaytime_t)priv->frame.delay * 10; - - // We need to get to the end of this frame - if (!priv->frame.posend) { - // We don't know where the end of the frame is yet - find it! - gfileSetPos(img->f, priv->frame.posimg+1); // Skip the code size byte too - while(1) { - if (gfileRead(img->f, &blocksz, 1) != 1) - return TIME_INFINITE; - if (!blocksz) - break; - gfileSetPos(img->f, gfileGetPos(img->f) + blocksz); - } - priv->frame.posend = gfileGetPos(img->f); - } - - // Seek to the end of this frame - gfileSetPos(img->f, priv->frame.posend); - - // Read the next frame descriptor - for(blocksz=0; blocksz < 2; blocksz++) { // 2 loops max to prevent cycling forever with a bad file - switch(initFrame(img)) { - case GDISP_IMAGE_ERR_OK: // Everything OK - return delay; - case GDISP_IMAGE_LOOP: // Back to the beginning - break; - case GDISP_IMAGE_EOF: // The real End-Of-File - case GDISP_IMAGE_ERR_BADDATA: // Oops - something wrong with the data - case GDISP_IMAGE_ERR_NOMEMORY: // Out of Memory - case GDISP_IMAGE_ERR_UNSUPPORTED: // Unsupported - default: - return TIME_INFINITE; - } - } - return TIME_INFINITE; -} - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_GIF */ -/** @} */ diff --git a/src/gdisp/image_jpg.c b/src/gdisp/image_jpg.c deleted file mode 100644 index 2f6a9392..00000000 --- a/src/gdisp/image_jpg.c +++ /dev/null @@ -1,19 +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/image_jpg.c - * @brief GDISP native image code. - */ -#include "gfx.h" - -#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG - -#error "JPG support not implemented yet" - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG */ -/** @} */ diff --git a/src/gdisp/image_native.c b/src/gdisp/image_native.c deleted file mode 100644 index 81344642..00000000 --- a/src/gdisp/image_native.c +++ /dev/null @@ -1,145 +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/image_native.c - * @brief GDISP native image code. - */ -#include "gfx.h" - -#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_NATIVE - -/** - * How big a pixel array to allocate for blitting - * Bigger is faster but uses more RAM. - */ -#define BLIT_BUFFER_SIZE 32 - -#define HEADER_SIZE 8 -#define FRAME0POS (HEADER_SIZE) - -/** - * Helper Routines Needed - */ -void *gdispImageAlloc(gdispImage *img, size_t sz); -void gdispImageFree(gdispImage *img, void *ptr, size_t sz); - -typedef struct gdispImagePrivate { - pixel_t *frame0cache; - pixel_t buf[BLIT_BUFFER_SIZE]; - } gdispImagePrivate; - -void gdispImageClose_NATIVE(gdispImage *img) { - if (img->priv) { - if (img->priv->frame0cache) - gdispImageFree(img, (void *)img->priv->frame0cache, img->width * img->height * sizeof(pixel_t)); - gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); - img->priv = 0; - } -} - -gdispImageError gdispImageOpen_NATIVE(gdispImage *img) { - uint8_t hdr[HEADER_SIZE]; - - /* Read the 8 byte header */ - if (gfileRead(img->f, hdr, 8) != 8) - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - if (hdr[0] != 'N' || hdr[1] != 'I') - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - if (hdr[6] != GDISP_PIXELFORMAT/256 || hdr[7] != (GDISP_PIXELFORMAT & 0xFF)) - return GDISP_IMAGE_ERR_UNSUPPORTED; // Unsupported pixel format - - /* We know we are a native format image */ - img->flags = 0; - img->width = (((uint16_t)hdr[2])<<8) | (hdr[3]); - img->height = (((uint16_t)hdr[4])<<8) | (hdr[5]); - if (img->width < 1 || img->height < 1) - return GDISP_IMAGE_ERR_BADDATA; - if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) - return GDISP_IMAGE_ERR_NOMEMORY; - img->priv->frame0cache = 0; - - img->type = GDISP_IMAGE_TYPE_NATIVE; - return GDISP_IMAGE_ERR_OK; -} - -gdispImageError gdispImageCache_NATIVE(gdispImage *img) { - size_t len; - - /* If we are already cached - just return OK */ - if (img->priv->frame0cache) - return GDISP_IMAGE_ERR_OK; - - /* We need to allocate the cache */ - len = img->width * img->height * sizeof(pixel_t); - img->priv->frame0cache = (pixel_t *)gdispImageAlloc(img, len); - if (!img->priv->frame0cache) - return GDISP_IMAGE_ERR_NOMEMORY; - - /* Read the entire bitmap into cache */ - gfileSetPos(img->f, FRAME0POS); - if (gfileRead(img->f, img->priv->frame0cache, len) != len) - return GDISP_IMAGE_ERR_BADDATA; - - return GDISP_IMAGE_ERR_OK; -} - -gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { - coord_t mx, mcx; - size_t pos, len; - - /* Check some reasonableness */ - if (sx >= img->width || sy >= img->height) return GDISP_IMAGE_ERR_OK; - if (sx + cx > img->width) cx = img->width - sx; - if (sy + cy > img->height) cy = img->height - sy; - - /* Draw from the image cache - if it exists */ - if (img->priv->frame0cache) { - gdispGBlitArea(g, x, y, cx, cy, sx, sy, img->width, img->priv->frame0cache); - return GDISP_IMAGE_ERR_OK; - } - - /* For this image decoder we cheat and just seek straight to the region we want to display */ - pos = FRAME0POS + (img->width * sy + sx) * sizeof(pixel_t); - - /* Cycle through the lines */ - for(;cy;cy--, y++) { - /* Move to the start of the line */ - gfileSetPos(img->f, pos); - - /* Draw the line in chunks using BitBlt */ - for(mx = x, mcx = cx; mcx > 0; mcx -= len, mx += len) { - // Read the data - len = gfileRead(img->f, - img->priv->buf, - mcx > BLIT_BUFFER_SIZE ? (BLIT_BUFFER_SIZE*sizeof(pixel_t)) : (mcx * sizeof(pixel_t))) - / sizeof(pixel_t); - if (!len) - return GDISP_IMAGE_ERR_BADDATA; - - /* Blit the chunk of data */ - gdispGBlitArea(g, mx, y, len, 1, 0, 0, len, img->priv->buf); - } - - /* Get the position for the start of the next line */ - pos += img->width*sizeof(pixel_t); - } - - return GDISP_IMAGE_ERR_OK; -} - -delaytime_t gdispImageNext_NATIVE(gdispImage *img) { - (void) img; - - /* No more frames/pages */ - return TIME_INFINITE; -} - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_NATIVE */ -/** @} */ diff --git a/src/gdisp/image_png.c b/src/gdisp/image_png.c deleted file mode 100644 index f35174d9..00000000 --- a/src/gdisp/image_png.c +++ /dev/null @@ -1,19 +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/image_png.c - * @brief GDISP native image code. - */ -#include "gfx.h" - -#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_PNG - -#error "PNG support not implemented yet" - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_PNG */ -/** @} */ diff --git a/src/gdisp/sys_defs.h b/src/gdisp/sys_defs.h index 72fdc621..253de49e 100644 --- a/src/gdisp/sys_defs.h +++ b/src/gdisp/sys_defs.h @@ -185,7 +185,7 @@ extern GDisplay *GDISP; /*===========================================================================*/ /* Load our color definitions and pixel formats */ -#include "colors.h" +#include "gdisp_colors.h" /** * @brief The type of a pixel. @@ -995,7 +995,7 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co #endif #if GDISP_NEED_IMAGE || defined(__DOXYGEN__) - #include "src/gdisp/image.h" + #include "gdisp_image.h" #endif #endif /* GFX_USE_GDISP */ diff --git a/src/gdisp/sys_make.mk b/src/gdisp/sys_make.mk index a133ce37..cbce4224 100644 --- a/src/gdisp/sys_make.mk +++ b/src/gdisp/sys_make.mk @@ -1,11 +1,11 @@ -GFXSRC += $(GFXLIB)/src/gdisp/gdisp.c \ - $(GFXLIB)/src/gdisp/fonts.c \ - $(GFXLIB)/src/gdisp/image.c \ - $(GFXLIB)/src/gdisp/image_native.c \ - $(GFXLIB)/src/gdisp/image_gif.c \ - $(GFXLIB)/src/gdisp/image_bmp.c \ - $(GFXLIB)/src/gdisp/image_jpg.c \ - $(GFXLIB)/src/gdisp/image_png.c +GFXSRC += $(GFXLIB)/src/gdisp/gdisp_gdisp.c \ + $(GFXLIB)/src/gdisp/gdisp_fonts.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 diff --git a/src/gevent/gevent.c b/src/gevent/gevent.c deleted file mode 100644 index 0a6ebcc6..00000000 --- a/src/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.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_gevent.c b/src/gevent/gevent_gevent.c new file mode 100644 index 00000000..fc45102e --- /dev/null +++ b/src/gevent/gevent_gevent.c @@ -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_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/sys_make.mk b/src/gevent/sys_make.mk index 5744ae46..f8b88e56 100644 --- a/src/gevent/sys_make.mk +++ b/src/gevent/sys_make.mk @@ -1 +1 @@ -GFXSRC += $(GFXLIB)/src/gevent/gevent.c +GFXSRC += $(GFXLIB)/src/gevent/gevent_gevent.c diff --git a/src/gfile/fatfs_chibios_diskio.c b/src/gfile/fatfs_chibios_diskio.c deleted file mode 100644 index 803c96a0..00000000 --- a/src/gfile/fatfs_chibios_diskio.c +++ /dev/null @@ -1,254 +0,0 @@ -/*-----------------------------------------------------------------------*/ -/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ -/*-----------------------------------------------------------------------*/ -/* This is a stub disk I/O module that acts as front end of the existing */ -/* disk I/O modules and attach it to FatFs module with common interface. */ -/*-----------------------------------------------------------------------*/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_FATFS && GFX_USE_OS_CHIBIOS - -#include "fatfs_wrapper.h" - -#if HAL_USE_MMC_SPI && HAL_USE_SDC -#error "cannot specify both MMC_SPI and SDC drivers" -#endif - -#if HAL_USE_MMC_SPI -extern MMCDriver MMCD1; -#elif HAL_USE_SDC -extern SDCDriver SDCD1; -#else -#error "MMC_SPI or SDC driver must be specified" -#endif - -#if HAL_USE_RTC -#include "chrtclib.h" -extern RTCDriver RTCD1; -#endif - -/*-----------------------------------------------------------------------*/ -/* Correspondence between physical drive number and physical drive. */ - -#define MMC 0 -#define SDC 0 - -/*-----------------------------------------------------------------------*/ -/* Initialize a Drive */ - -DSTATUS disk_initialize ( - BYTE drv /* Physical drive nmuber (0..) */ -) -{ - DSTATUS stat; - - switch (drv) { -#if HAL_USE_MMC_SPI - case MMC: - stat = 0; - /* It is initialized externally, just reads the status.*/ - if (blkGetDriverState(&MMCD1) != BLK_READY) - stat |= STA_NOINIT; - if (mmcIsWriteProtected(&MMCD1)) - stat |= STA_PROTECT; - return stat; -#else - case SDC: - stat = 0; - /* It is initialized externally, just reads the status.*/ - if (blkGetDriverState(&SDCD1) != BLK_READY) - stat |= STA_NOINIT; - if (sdcIsWriteProtected(&SDCD1)) - stat |= STA_PROTECT; - return stat; -#endif - } - return STA_NODISK; -} - - - -/*-----------------------------------------------------------------------*/ -/* Return Disk Status */ - -DSTATUS disk_status ( - BYTE drv /* Physical drive nmuber (0..) */ -) -{ - DSTATUS stat; - - switch (drv) { -#if HAL_USE_MMC_SPI - case MMC: - stat = 0; - /* It is initialized externally, just reads the status.*/ - if (blkGetDriverState(&MMCD1) != BLK_READY) - stat |= STA_NOINIT; - if (mmcIsWriteProtected(&MMCD1)) - stat |= STA_PROTECT; - return stat; -#else - case SDC: - stat = 0; - /* It is initialized externally, just reads the status.*/ - if (blkGetDriverState(&SDCD1) != BLK_READY) - stat |= STA_NOINIT; - if (sdcIsWriteProtected(&SDCD1)) - stat |= STA_PROTECT; - return stat; -#endif - } - return STA_NODISK; -} - - - -/*-----------------------------------------------------------------------*/ -/* Read Sector(s) */ - -DRESULT disk_read ( - BYTE drv, /* Physical drive nmuber (0..) */ - BYTE *buff, /* Data buffer to store read data */ - DWORD sector, /* Sector address (LBA) */ - UINT count /* Number of sectors to read (1..255) */ -) -{ - switch (drv) { -#if HAL_USE_MMC_SPI - case MMC: - if (blkGetDriverState(&MMCD1) != BLK_READY) - return RES_NOTRDY; - if (mmcStartSequentialRead(&MMCD1, sector)) - return RES_ERROR; - while (count > 0) { - if (mmcSequentialRead(&MMCD1, buff)) - return RES_ERROR; - buff += MMCSD_BLOCK_SIZE; - count--; - } - if (mmcStopSequentialRead(&MMCD1)) - return RES_ERROR; - return RES_OK; -#else - case SDC: - if (blkGetDriverState(&SDCD1) != BLK_READY) - return RES_NOTRDY; - if (sdcRead(&SDCD1, sector, buff, count)) - return RES_ERROR; - return RES_OK; -#endif - } - return RES_PARERR; -} - - - -/*-----------------------------------------------------------------------*/ -/* Write Sector(s) */ - -#if _READONLY == 0 -DRESULT disk_write ( - BYTE drv, /* Physical drive nmuber (0..) */ - const BYTE *buff, /* Data to be written */ - DWORD sector, /* Sector address (LBA) */ - UINT count /* Number of sectors to write (1..255) */ -) -{ - switch (drv) { -#if HAL_USE_MMC_SPI - case MMC: - if (blkGetDriverState(&MMCD1) != BLK_READY) - return RES_NOTRDY; - if (mmcIsWriteProtected(&MMCD1)) - return RES_WRPRT; - if (mmcStartSequentialWrite(&MMCD1, sector)) - return RES_ERROR; - while (count > 0) { - if (mmcSequentialWrite(&MMCD1, buff)) - return RES_ERROR; - buff += MMCSD_BLOCK_SIZE; - count--; - } - if (mmcStopSequentialWrite(&MMCD1)) - return RES_ERROR; - return RES_OK; -#else - case SDC: - if (blkGetDriverState(&SDCD1) != BLK_READY) - return RES_NOTRDY; - if (sdcWrite(&SDCD1, sector, buff, count)) - return RES_ERROR; - return RES_OK; -#endif - } - return RES_PARERR; -} -#endif /* _READONLY */ - - - -/*-----------------------------------------------------------------------*/ -/* Miscellaneous Functions */ - -DRESULT disk_ioctl ( - BYTE drv, /* Physical drive nmuber (0..) */ - BYTE ctrl, /* Control code */ - void *buff /* Buffer to send/receive control data */ -) -{ - switch (drv) { -#if HAL_USE_MMC_SPI - case MMC: - switch (ctrl) { - case CTRL_SYNC: - return RES_OK; - case GET_SECTOR_SIZE: - *((WORD *)buff) = MMCSD_BLOCK_SIZE; - return RES_OK; -#if _USE_ERASE - case CTRL_ERASE_SECTOR: - mmcErase(&MMCD1, *((DWORD *)buff), *((DWORD *)buff + 1)); - return RES_OK; -#endif - default: - return RES_PARERR; - } -#else - case SDC: - switch (ctrl) { - case CTRL_SYNC: - return RES_OK; - case GET_SECTOR_COUNT: - *((DWORD *)buff) = mmcsdGetCardCapacity(&SDCD1); - return RES_OK; - case GET_SECTOR_SIZE: - *((WORD *)buff) = MMCSD_BLOCK_SIZE; - return RES_OK; - case GET_BLOCK_SIZE: - *((DWORD *)buff) = 256; /* 512b blocks in one erase block */ - return RES_OK; -#if _USE_ERASE - case CTRL_ERASE_SECTOR: - sdcErase(&SDCD1, *((DWORD *)buff), *((DWORD *)buff + 1)); - return RES_OK; -#endif - default: - return RES_PARERR; - } -#endif - } - return RES_PARERR; -} - -DWORD get_fattime(void) { -#if HAL_USE_RTC - return rtcGetTimeFat(&RTCD1); -#else - return ((uint32_t)0 | (1 << 16)) | (1 << 21); /* wrong but valid time */ -#endif -} - -#endif // GFX_USE_GFILE && GFILE_NEED_FATFS && GFX_USE_OS_CHIBIOS - - diff --git a/src/gfile/fatfs_wrapper.c b/src/gfile/fatfs_wrapper.c deleted file mode 100644 index 3ac8e427..00000000 --- a/src/gfile/fatfs_wrapper.c +++ /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/gfile/fatfs_wrapper.c - * @brief GFILE FATFS wrapper. - * - */ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_FATFS - -#include "fatfs_wrapper.h" - -// Include the source we want -#include "3rdparty/fatfs-0.10b/src/ff.c" -#include "3rdparty/fatfs-0.10b/src/option/unicode.c" - -// Extra operating system support -#if _FS_REENTRANT - /*------------------------------------------------------------------------*/ - /* Static array of Synchronization Objects */ - /*------------------------------------------------------------------------*/ - static gfxSem ff_sem[_VOLUMES]; - - /*------------------------------------------------------------------------*/ - /* Create a Synchronization Object */ - /*------------------------------------------------------------------------*/ - int ff_cre_syncobj(BYTE vol, _SYNC_t *sobj) - { - *sobj = ff_sem[vol]; - gfxSemInit(sobj, 1, MAX_SEMAPHORE_COUNT); - - return 1; - } - - /*------------------------------------------------------------------------*/ - /* Delete a Synchronization Object */ - /*------------------------------------------------------------------------*/ - int ff_del_syncobj(_SYNC_t sobj) - { - gfxSemDestroy( (gfxSem*)&sobj ); - - return 1; - } - - /*------------------------------------------------------------------------*/ - /* Request Grant to Access the Volume */ - /*------------------------------------------------------------------------*/ - int ff_req_grant(_SYNC_t sobj) - { - if (gfxSemWait( (gfxSem*)&sobj, (delaytime_t)_FS_TIMEOUT) ) - return TRUE; - return FALSE; - } - - /*------------------------------------------------------------------------*/ - /* Release Grant to Access the Volume */ - /*------------------------------------------------------------------------*/ - void ff_rel_grant(_SYNC_t sobj) - { - gfxSemSignal( (gfxSem*)&sobj ); - } -#endif /* _FS_REENTRANT */ - -#if _USE_LFN == 3 /* LFN with a working buffer on the heap */ - /*------------------------------------------------------------------------*/ - /* Allocate a memory block */ - /*------------------------------------------------------------------------*/ - void *ff_memalloc(UINT size) - { - return gfxAlloc( (size_t)size ); - } - - /*------------------------------------------------------------------------*/ - /* Free a memory block */ - /*------------------------------------------------------------------------*/ - void ff_memfree(void *mblock) - { - gfxFree(mblock); - } -#endif /* _USE_LFN == 3 */ - -#endif // GFX_USE_GFILE && GFILE_NEED_FATFS diff --git a/src/gfile/fatfs_wrapper.h b/src/gfile/fatfs_wrapper.h deleted file mode 100644 index fc08815b..00000000 --- a/src/gfile/fatfs_wrapper.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/gfile/fatfs_wrapper.h - * @brief GFILE FATFS wrapper. - * - */ - -#ifndef _FATFS_WRAPPER -#define _FATFS_WRAPPER - -// Include the fatfs configuration from the local directory not the original source folder -#include "ffconf.h" - -// Prevent preprocessor redefinition warnings -#include "3rdparty/fatfs-0.10b/src/integer.h" -#if defined(_T) && !defined(_INC_TCHAR) - #define _INC_TCHAR -#endif - -// Include the fatfs API -#include "3rdparty/fatfs-0.10b/src/ff.h" - -// Include the fatfs diskio API -#include "3rdparty/fatfs-0.10b/src/diskio.h" - -#endif //_FATFS_WRAPPER diff --git a/src/gfile/gfile.c b/src/gfile/gfile.c deleted file mode 100644 index 595bba1f..00000000 --- a/src/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.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_fatfs_diskio_chibios.c b/src/gfile/gfile_fatfs_diskio_chibios.c new file mode 100644 index 00000000..46ddbb7e --- /dev/null +++ b/src/gfile/gfile_fatfs_diskio_chibios.c @@ -0,0 +1,260 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* This is a stub disk I/O module that acts as front end of the existing */ +/* 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 + +#include "gfile_fatfs_wrapper.h" + +#if HAL_USE_MMC_SPI && HAL_USE_SDC +#error "cannot specify both MMC_SPI and SDC drivers" +#endif + +#if HAL_USE_MMC_SPI +extern MMCDriver MMCD1; +#elif HAL_USE_SDC +extern SDCDriver SDCD1; +#else +#error "MMC_SPI or SDC driver must be specified" +#endif + +#if HAL_USE_RTC +#include "chrtclib.h" +extern RTCDriver RTCD1; +#endif + +/*-----------------------------------------------------------------------*/ +/* Correspondence between physical drive number and physical drive. */ + +#define MMC 0 +#define SDC 0 + +/*-----------------------------------------------------------------------*/ +/* Initialize a Drive */ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + DSTATUS stat; + + switch (drv) { +#if HAL_USE_MMC_SPI + case MMC: + stat = 0; + /* It is initialized externally, just reads the status.*/ + if (blkGetDriverState(&MMCD1) != BLK_READY) + stat |= STA_NOINIT; + if (mmcIsWriteProtected(&MMCD1)) + stat |= STA_PROTECT; + return stat; +#else + case SDC: + stat = 0; + /* It is initialized externally, just reads the status.*/ + if (blkGetDriverState(&SDCD1) != BLK_READY) + stat |= STA_NOINIT; + if (sdcIsWriteProtected(&SDCD1)) + stat |= STA_PROTECT; + return stat; +#endif + } + return STA_NODISK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Return Disk Status */ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + DSTATUS stat; + + switch (drv) { +#if HAL_USE_MMC_SPI + case MMC: + stat = 0; + /* It is initialized externally, just reads the status.*/ + if (blkGetDriverState(&MMCD1) != BLK_READY) + stat |= STA_NOINIT; + if (mmcIsWriteProtected(&MMCD1)) + stat |= STA_PROTECT; + return stat; +#else + case SDC: + stat = 0; + /* It is initialized externally, just reads the status.*/ + if (blkGetDriverState(&SDCD1) != BLK_READY) + stat |= STA_NOINIT; + if (sdcIsWriteProtected(&SDCD1)) + stat |= STA_PROTECT; + return stat; +#endif + } + return STA_NODISK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE *buff, /* Data buffer to store read data */ + DWORD sector, /* Sector address (LBA) */ + UINT count /* Number of sectors to read (1..255) */ +) +{ + switch (drv) { +#if HAL_USE_MMC_SPI + case MMC: + if (blkGetDriverState(&MMCD1) != BLK_READY) + return RES_NOTRDY; + if (mmcStartSequentialRead(&MMCD1, sector)) + return RES_ERROR; + while (count > 0) { + if (mmcSequentialRead(&MMCD1, buff)) + return RES_ERROR; + buff += MMCSD_BLOCK_SIZE; + count--; + } + if (mmcStopSequentialRead(&MMCD1)) + return RES_ERROR; + return RES_OK; +#else + case SDC: + if (blkGetDriverState(&SDCD1) != BLK_READY) + return RES_NOTRDY; + if (sdcRead(&SDCD1, sector, buff, count)) + return RES_ERROR; + return RES_OK; +#endif + } + return RES_PARERR; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0..) */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Sector address (LBA) */ + UINT count /* Number of sectors to write (1..255) */ +) +{ + switch (drv) { +#if HAL_USE_MMC_SPI + case MMC: + if (blkGetDriverState(&MMCD1) != BLK_READY) + return RES_NOTRDY; + if (mmcIsWriteProtected(&MMCD1)) + return RES_WRPRT; + if (mmcStartSequentialWrite(&MMCD1, sector)) + return RES_ERROR; + while (count > 0) { + if (mmcSequentialWrite(&MMCD1, buff)) + return RES_ERROR; + buff += MMCSD_BLOCK_SIZE; + count--; + } + if (mmcStopSequentialWrite(&MMCD1)) + return RES_ERROR; + return RES_OK; +#else + case SDC: + if (blkGetDriverState(&SDCD1) != BLK_READY) + return RES_NOTRDY; + if (sdcWrite(&SDCD1, sector, buff, count)) + return RES_ERROR; + return RES_OK; +#endif + } + return RES_PARERR; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + switch (drv) { +#if HAL_USE_MMC_SPI + case MMC: + switch (ctrl) { + case CTRL_SYNC: + return RES_OK; + case GET_SECTOR_SIZE: + *((WORD *)buff) = MMCSD_BLOCK_SIZE; + return RES_OK; +#if _USE_ERASE + case CTRL_ERASE_SECTOR: + mmcErase(&MMCD1, *((DWORD *)buff), *((DWORD *)buff + 1)); + return RES_OK; +#endif + default: + return RES_PARERR; + } +#else + case SDC: + switch (ctrl) { + case CTRL_SYNC: + return RES_OK; + case GET_SECTOR_COUNT: + *((DWORD *)buff) = mmcsdGetCardCapacity(&SDCD1); + return RES_OK; + case GET_SECTOR_SIZE: + *((WORD *)buff) = MMCSD_BLOCK_SIZE; + return RES_OK; + case GET_BLOCK_SIZE: + *((DWORD *)buff) = 256; /* 512b blocks in one erase block */ + return RES_OK; +#if _USE_ERASE + case CTRL_ERASE_SECTOR: + sdcErase(&SDCD1, *((DWORD *)buff), *((DWORD *)buff + 1)); + return RES_OK; +#endif + default: + return RES_PARERR; + } +#endif + } + return RES_PARERR; +} + +DWORD get_fattime(void) { +#if HAL_USE_RTC + return rtcGetTimeFat(&RTCD1); +#else + return ((uint32_t)0 | (1 << 16)) | (1 << 21); /* wrong but valid time */ +#endif +} + +#endif // 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 new file mode 100644 index 00000000..edcab056 --- /dev/null +++ b/src/gfile/gfile_fatfs_wrapper.c @@ -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/gfile/gfile_fatfs_wrapper.c + * @brief GFILE FATFS wrapper. + * + */ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_FATFS + +#include "gfile_fatfs_wrapper.h" + +// Include the source we want +#include "3rdparty/fatfs-0.10b/src/ff.c" +#include "3rdparty/fatfs-0.10b/src/option/unicode.c" + +// Extra operating system support +#if _FS_REENTRANT + /*------------------------------------------------------------------------*/ + /* Static array of Synchronization Objects */ + /*------------------------------------------------------------------------*/ + static gfxSem ff_sem[_VOLUMES]; + + /*------------------------------------------------------------------------*/ + /* Create a Synchronization Object */ + /*------------------------------------------------------------------------*/ + int ff_cre_syncobj(BYTE vol, _SYNC_t *sobj) + { + *sobj = ff_sem[vol]; + gfxSemInit(sobj, 1, MAX_SEMAPHORE_COUNT); + + return 1; + } + + /*------------------------------------------------------------------------*/ + /* Delete a Synchronization Object */ + /*------------------------------------------------------------------------*/ + int ff_del_syncobj(_SYNC_t sobj) + { + gfxSemDestroy( (gfxSem*)&sobj ); + + return 1; + } + + /*------------------------------------------------------------------------*/ + /* Request Grant to Access the Volume */ + /*------------------------------------------------------------------------*/ + int ff_req_grant(_SYNC_t sobj) + { + if (gfxSemWait( (gfxSem*)&sobj, (delaytime_t)_FS_TIMEOUT) ) + return TRUE; + return FALSE; + } + + /*------------------------------------------------------------------------*/ + /* Release Grant to Access the Volume */ + /*------------------------------------------------------------------------*/ + void ff_rel_grant(_SYNC_t sobj) + { + gfxSemSignal( (gfxSem*)&sobj ); + } +#endif /* _FS_REENTRANT */ + +#if _USE_LFN == 3 /* LFN with a working buffer on the heap */ + /*------------------------------------------------------------------------*/ + /* Allocate a memory block */ + /*------------------------------------------------------------------------*/ + void *ff_memalloc(UINT size) + { + return gfxAlloc( (size_t)size ); + } + + /*------------------------------------------------------------------------*/ + /* Free a memory block */ + /*------------------------------------------------------------------------*/ + void ff_memfree(void *mblock) + { + gfxFree(mblock); + } +#endif /* _USE_LFN == 3 */ + +#endif // GFX_USE_GFILE && GFILE_NEED_FATFS diff --git a/src/gfile/gfile_fatfs_wrapper.h b/src/gfile/gfile_fatfs_wrapper.h new file mode 100644 index 00000000..e66e13bc --- /dev/null +++ b/src/gfile/gfile_fatfs_wrapper.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/gfile/gfile_fatfs_wrapper.h + * @brief GFILE FATFS wrapper. + * + */ + +#ifndef _FATFS_WRAPPER +#define _FATFS_WRAPPER + +// Include the fatfs configuration from the local directory not the original source folder +#include "ffconf.h" + +// Prevent preprocessor redefinition warnings +#include "3rdparty/fatfs-0.10b/src/integer.h" +#if defined(_T) && !defined(_INC_TCHAR) + #define _INC_TCHAR +#endif + +// Include the fatfs API +#include "3rdparty/fatfs-0.10b/src/ff.h" + +// Include the fatfs diskio API +#include "3rdparty/fatfs-0.10b/src/diskio.h" + +#endif //_FATFS_WRAPPER diff --git a/src/gfile/gfile_fs_chibios.c b/src/gfile/gfile_fs_chibios.c new file mode 100644 index 00000000..72826e28 --- /dev/null +++ b/src/gfile/gfile_fs_chibios.c @@ -0,0 +1,68 @@ +/* + * 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 + */ + +/******************************************************** + * The ChibiOS BaseFileStream file-system + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS + +#include "gfile_fs.h" + +static void ChibiOSBFSClose(GFILE *f); +static int ChibiOSBFSRead(GFILE *f, void *buf, int size); +static int ChibiOSBFSWrite(GFILE *f, const void *buf, int size); +static bool_t ChibiOSBFSSetpos(GFILE *f, long int pos); +static long int ChibiOSBFSGetsize(GFILE *f); +static bool_t ChibiOSBFSEof(GFILE *f); + +static const GFILEVMT FsCHIBIOSVMT = { + GFSFLG_SEEKABLE|GFSFLG_WRITEABLE, // flags + 0, // prefix + 0, 0, 0, 0, + 0, ChibiOSBFSClose, ChibiOSBFSRead, ChibiOSBFSWrite, + ChibiOSBFSSetpos, ChibiOSBFSGetsize, ChibiOSBFSEof, + 0, 0, 0, + #if GFILE_NEED_FILELISTS + 0, 0, 0, + #endif +}; + +static void ChibiOSBFSClose(GFILE *f) { + chFileStreamClose(((BaseFileStream *)f->obj)); +} +static int ChibiOSBFSRead(GFILE *f, void *buf, int size) { + return chSequentialStreamRead(((BaseFileStream *)f->obj), (uint8_t *)buf, size); +} +static int ChibiOSBFSWrite(GFILE *f, const void *buf, int size) { + return chSequentialStreamWrite(((BaseFileStream *)f->obj), (uint8_t *)buf, size); +} +static bool_t ChibiOSBFSSetpos(GFILE *f, long int pos) { + chFileStreamSeek(((BaseFileStream *)f->obj), pos); + return TRUE; +} +static long int ChibiOSBFSGetsize(GFILE *f) { return chFileStreamGetSize(((BaseFileStream *)f->obj)); } +static bool_t ChibiOSBFSEof(GFILE *f) { return f->pos >= chFileStreamGetSize(((BaseFileStream *)f->obj)); } + +GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode) { + GFILE * f; + + // Get an empty file and set the flags + if (!(f = _gfileFindSlot(mode))) + return 0; + + // File is open - fill in all the details + f->vmt = &FsCHIBIOSVMT; + f->obj = BaseFileStreamPtr; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; + return f; +} + +#endif //GFX_USE_GFILE && GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS diff --git a/src/gfile/gfile_fs_fatfs.c b/src/gfile/gfile_fs_fatfs.c new file mode 100644 index 00000000..2e4ef52b --- /dev/null +++ b/src/gfile/gfile_fs_fatfs.c @@ -0,0 +1,325 @@ +/* + * 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 + */ + +/******************************************************** + * The FATFS file-system + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_FATFS + +#include "gfile_fs.h" +#include "fatfs_wrapper.h" + +/******************************************************** + * The FAT file-system VMT + ********************************************************/ + +static bool_t fatfsDel(const char* fname); +static bool_t fatfsExists(const char* fname); +static long int fatfsFileSize(const char* fname); +static bool_t fatfsRename(const char* oldname, const char* newname); +static bool_t fatfsOpen(GFILE* f, const char* fname); +static void fatfsClose(GFILE* f); +static int fatfsRead(GFILE* f, void* buf, int size); +static int fatfsWrite(GFILE* f, const void* buf, int size); +static bool_t fatfsSetPos(GFILE* f, long int pos); +static long int fatfsGetSize(GFILE* f); +static bool_t fatfsEOF(GFILE* f); +static bool_t fatfsMount(const char* drive); +static bool_t fatfsUnmount(const char* drive); +static bool_t fatfsSync(GFILE* f); +#if GFILE_NEED_FILELISTS && _FS_MINIMIZE <= 1 + static gfileList *fatfsFlOpen(const char *path, bool_t dirs); + static const char *fatfsFlRead(gfileList *pfl); + static void fatfsFlClose(gfileList *pfl); +#endif + +const GFILEVMT FsFatFSVMT = { + GFSFLG_WRITEABLE | GFSFLG_SEEKABLE, + 'F', + fatfsDel, + fatfsExists, + fatfsFileSize, + fatfsRename, + fatfsOpen, + fatfsClose, + fatfsRead, + fatfsWrite, + fatfsSetPos, + fatfsGetSize, + fatfsEOF, + fatfsMount, fatfsUnmount, fatfsSync, + #if GFILE_NEED_FILELISTS + #if _FS_MINIMIZE <= 1 + fatfsFlOpen, fatfsFlRead, fatfsFlClose + #else + 0, 0, 0 + #endif + #endif +}; + +// Our directory list structure +typedef struct fatfsList { + gfileList fl; // This must be the first element. + DIR dir; + FILINFO fno; + #if _USE_LFN + char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */ + #endif +} fatfsList; + +// optimize these later on. Use an array to have multiple FatFS +static bool_t fatfs_mounted = FALSE; +static FATFS fatfs_fs; + +static BYTE fatfs_flags2mode(GFILE* f) +{ + BYTE mode = 0; + + if (f->flags & GFILEFLG_READ) + mode |= FA_READ; + if (f->flags & GFILEFLG_WRITE) + mode |= FA_WRITE; + if (f->flags & GFILEFLG_APPEND) + mode |= 0; // ToDo + if (f->flags & GFILEFLG_TRUNC) + mode |= FA_CREATE_ALWAYS; + + /* ToDo - Complete */ + return mode; +} + +static bool_t fatfsDel(const char* fname) +{ + FRESULT ferr; + + ferr = f_unlink( (const TCHAR*)fname ); + if (ferr != FR_OK) + return FALSE; + + return TRUE; +} + +static bool_t fatfsExists(const char* fname) +{ + FRESULT ferr; + FILINFO fno; + + ferr = f_stat( (const TCHAR*)fname, &fno); + if (ferr != FR_OK) + return FALSE; + + return TRUE; +} + +static long int fatfsFileSize(const char* fname) +{ + FRESULT ferr; + FILINFO fno; + + ferr = f_stat( (const TCHAR*)fname, &fno ); + if (ferr != FR_OK) + return 0; + + return (long int)fno.fsize; +} + +static bool_t fatfsRename(const char* oldname, const char* newname) +{ + FRESULT ferr; + + ferr = f_rename( (const TCHAR*)oldname, (const TCHAR*)newname ); + if (ferr != FR_OK) + return FALSE; + + return TRUE; +} + +static bool_t fatfsOpen(GFILE* f, const char* fname) +{ + FIL* fd; + + #if !GFILE_NEED_NOAUTOMOUNT + if (!fatfs_mounted && !fatfsMount("")) + return FALSE; + #endif + + if (!(fd = gfxAlloc(sizeof(FIL)))) + return FALSE; + + if (f_open(fd, fname, fatfs_flags2mode(f)) != FR_OK) { + gfxFree(fd); + f->obj = 0; + + return FALSE; + } + + f->obj = (void*)fd; + + #if !GFILE_NEED_NOAUTOSYNC + // no need to sync when not opening for write + if (f->flags & GFILEFLG_WRITE) { + f_sync( (FIL*)f->obj ); + } + #endif + + return TRUE; +} + +static void fatfsClose(GFILE* f) +{ + if ((FIL*)f->obj != 0) { + f_close( (FIL*)f->obj ); + gfxFree( (FIL*)f->obj ); + } +} + +static int fatfsRead(GFILE* f, void* buf, int size) +{ + int br; + + f_read( (FIL*)f->obj, buf, size, (UINT*)&br); + + return br; +} + +static int fatfsWrite(GFILE* f, const void* buf, int size) +{ + int wr; + + f_write( (FIL*)f->obj, buf, size, (UINT*)&wr); + #if !GFILE_NEED_NOAUTOSYNC + f_sync( (FIL*)f->obj ); + #endif + + return wr; +} + +static bool_t fatfsSetPos(GFILE* f, long int pos) +{ + FRESULT ferr; + + ferr = f_lseek( (FIL*)f->obj, (DWORD)pos ); + if (ferr != FR_OK) + return FALSE; + + return TRUE; +} + +static long int fatfsGetSize(GFILE* f) +{ + return (long int)f_size( (FIL*)f->obj ); +} + +static bool_t fatfsEOF(GFILE* f) +{ + if ( f_eof( (FIL*)f->obj ) != 0) + return TRUE; + else + return FALSE; +} + +static bool_t fatfsMount(const char* drive) +{ + FRESULT ferr; + + if (!fatfs_mounted) { + ferr = f_mount(&fatfs_fs, drive, 1); + if (ferr != FR_OK) + return FALSE; + fatfs_mounted = TRUE; + return TRUE; + } + + return FALSE; +} + +static bool_t fatfsUnmount(const char* drive) +{ + (void)drive; + + if (fatfs_mounted) { + // FatFS does not provide an unmount routine. + fatfs_mounted = FALSE; + return TRUE; + } + + return FALSE; +} + +static bool_t fatfsSync(GFILE *f) +{ + FRESULT ferr; + + ferr = f_sync( (FIL*)f->obj ); + if (ferr != FR_OK) { + return FALSE; + } + + return TRUE; +} + +#if GFILE_NEED_FILELISTS && _FS_MINIMIZE <= 1 + static gfileList *fatfsFlOpen(const char *path, bool_t dirs) { + fatfsList *p; + (void) dirs; + + if (!(p = gfxAlloc(sizeof(fatfsList)))) + return 0; + + if (f_opendir(&p->dir, path) != FR_OK) { + gfxFree(p); + return 0; + } + return &p->fl; + } + + static const char *fatfsFlRead(gfileList *pfl) { + #define ffl ((fatfsList *)pfl) + + while(1) { + #if _USE_LFN + ffl->fno.lfname = ffl->lfn; + ffl->fno.lfsize = sizeof(ffl->lfn); + #endif + + // Read the next entry + if (f_readdir(&ffl->dir, &ffl->fno) != FR_OK || !ffl->fno.fname[0]) + return 0; + + /* Ignore dot entries */ + if (ffl->fno.fname[0] == '.') continue; + + /* Is it a directory */ + if (ffl->fl.dirs) { + if ((ffl->fno.fattrib & AM_DIR)) + break; + } else { + if (!(ffl->fno.fattrib & AM_DIR)) + break; + } + } + + #if _USE_LFN + return ffl->fno.lfname[0] ? ffl->fno.lfname : ffl->fno.fname; + #else + return ffl->fno.fname; + #endif + #undef ffl + } + + static void fatfsFlClose(gfileList *pfl) { + f_closedir(&((fatfsList *)pfl)->dir); + gfxFree(pfl); + } + +#endif + +#endif //GFX_USE_GFILE && GFILE_NEED_FATFS + diff --git a/src/gfile/gfile_fs_mem.c b/src/gfile/gfile_fs_mem.c new file mode 100644 index 00000000..150220b8 --- /dev/null +++ b/src/gfile/gfile_fs_mem.c @@ -0,0 +1,65 @@ +/* + * 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 + */ + +/******************************************************** + * The virtual memory file-system + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_MEMFS + +#include "gfile_fs.h" + +#include + +static int MEMRead(GFILE *f, void *buf, int size); +static int MEMWrite(GFILE *f, const void *buf, int size); +static bool_t MEMSetpos(GFILE *f, long int pos); + +static const GFILEVMT FsMemVMT = { + GFSFLG_SEEKABLE|GFSFLG_WRITEABLE, // flags + 0, // prefix + 0, 0, 0, 0, + 0, 0, MEMRead, MEMWrite, + MEMSetpos, 0, 0, + 0, 0, 0, + #if GFILE_NEED_FILELISTS + 0, 0, 0, + #endif +}; + +static int MEMRead(GFILE *f, void *buf, int size) { + memcpy(buf, ((char *)f->obj)+f->pos, size); + return size; +} +static int MEMWrite(GFILE *f, const void *buf, int size) { + memcpy(((char *)f->obj)+f->pos, buf, size); + return size; +} +static bool_t MEMSetpos(GFILE *f, long int pos) { + (void) f; + (void) pos; + return TRUE; +} + +GFILE * gfileOpenMemory(void *memptr, const char *mode) { + GFILE *f; + + // Get an empty file and set the flags + if (!(f = _gfileFindSlot(mode))) + return 0; + + // File is open - fill in all the details + f->vmt = &FsMemVMT; + f->obj = memptr; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; + return f; +} + +#endif //GFX_USE_GFILE && GFILE_NEED_MEMFS diff --git a/src/gfile/gfile_fs_native.c b/src/gfile/gfile_fs_native.c new file mode 100644 index 00000000..6a9fce94 --- /dev/null +++ b/src/gfile/gfile_fs_native.c @@ -0,0 +1,234 @@ +/* + * 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 + */ + +/******************************************************** + * The native file-system + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_NATIVEFS + +#include "gfile_fs.h" + +#include +#include +#include + +static bool_t NativeDel(const char *fname); +static bool_t NativeExists(const char *fname); +static long int NativeFilesize(const char *fname); +static bool_t NativeRen(const char *oldname, const char *newname); +static bool_t NativeOpen(GFILE *f, const char *fname); +static void NativeClose(GFILE *f); +static int NativeRead(GFILE *f, void *buf, int size); +static int NativeWrite(GFILE *f, const void *buf, int size); +static bool_t NativeSetpos(GFILE *f, long int pos); +static long int NativeGetsize(GFILE *f); +static bool_t NativeEof(GFILE *f); +#if GFILE_NEED_FILELISTS + static gfileList *NativeFlOpen(const char *path, bool_t dirs); + static const char *NativeFlRead(gfileList *pfl); + static void NativeFlClose(gfileList *pfl); +#endif + +const GFILEVMT FsNativeVMT = { + #if defined(WIN32) || GFX_USE_OS_WIN32 + GFSFLG_TEXTMODES| + #else + GFSFLG_CASESENSITIVE| + #endif + GFSFLG_WRITEABLE|GFSFLG_SEEKABLE|GFSFLG_FAST, // flags + 'N', // prefix + NativeDel, NativeExists, NativeFilesize, NativeRen, + NativeOpen, NativeClose, NativeRead, NativeWrite, + NativeSetpos, NativeGetsize, NativeEof, + 0, 0, 0, + #if GFILE_NEED_FILELISTS + NativeFlOpen, NativeFlRead, NativeFlClose + #endif +}; + +void _gfileNativeAssignStdio(void) { + static GFILE NativeStdIn; + static GFILE NativeStdOut; + static GFILE NativeStdErr; + + NativeStdIn.flags = GFILEFLG_OPEN|GFILEFLG_READ; + NativeStdIn.vmt = &FsNativeVMT; + NativeStdIn.obj = (void *)stdin; + NativeStdIn.pos = 0; + gfileStdIn = &NativeStdIn; + NativeStdOut.flags = GFILEFLG_OPEN|GFILEFLG_WRITE|GFILEFLG_APPEND; + NativeStdOut.vmt = &FsNativeVMT; + NativeStdOut.obj = (void *)stdout; + NativeStdOut.pos = 0; + gfileStdOut = &NativeStdOut; + NativeStdErr.flags = GFILEFLG_OPEN|GFILEFLG_WRITE|GFILEFLG_APPEND; + NativeStdErr.vmt = &FsNativeVMT; + NativeStdErr.obj = (void *)stderr; + NativeStdErr.pos = 0; + gfileStdErr = &NativeStdErr; +} + +static void Native_flags2mode(char *buf, uint16_t flags) { + if (flags & GFILEFLG_MUSTEXIST) + *buf = 'r'; + else if (flags & GFILEFLG_APPEND) + *buf = 'a'; + else + *buf = 'w'; + buf++; + if ((flags & (GFILEFLG_READ|GFILEFLG_WRITE)) == (GFILEFLG_READ|GFILEFLG_WRITE)) + *buf++ = '+'; + if (flags & GFILEFLG_BINARY) + *buf++ = 'b'; + if (flags & GFILEFLG_MUSTNOTEXIST) + *buf++ = 'x'; + *buf++ = 0; +} + +static bool_t NativeDel(const char *fname) { return remove(fname) ? FALSE : TRUE; } +static void NativeClose(GFILE *f) { fclose((FILE *)f->obj); } +static int NativeRead(GFILE *f, void *buf, int size) { return fread(buf, 1, size, (FILE *)f->obj); } +static int NativeWrite(GFILE *f, const void *buf, int size) { return fwrite(buf, 1, size, (FILE *)f->obj); } +static bool_t NativeSetpos(GFILE *f, long int pos) { return fseek((FILE *)f->obj, pos, SEEK_SET) ? FALSE : TRUE; } +static bool_t NativeEof(GFILE *f) { return feof((FILE *)f->obj) ? TRUE : FALSE; } +static bool_t NativeRen(const char *oldname, const char *newname) { return rename(oldname, newname) ? FALSE : TRUE; } +static bool_t NativeExists(const char *fname) { + // We define access this way so we don't have to include which may + // (and does under windows) contain conflicting definitions for types such as uint16_t. + extern int access(const char *pathname, int mode); + return access(fname, 0) ? FALSE : TRUE; +} +static long int NativeFilesize(const char *fname) { + struct stat st; + if (stat(fname, &st)) return -1; + return st.st_size; +} +static bool_t NativeOpen(GFILE *f, const char *fname) { + FILE *fd; + char mode[5]; + + Native_flags2mode(mode, f->flags); + if (!(fd = fopen(fname, mode))) + return FALSE; + f->obj = (void *)fd; + return TRUE; +} +static long int NativeGetsize(GFILE *f) { + struct stat st; + if (fstat(fileno((FILE *)f->obj), &st)) return -1; + return st.st_size; +} + +#if GFILE_NEED_FILELISTS + #if defined(WIN32) || GFX_USE_OS_WIN32 + typedef struct NativeFileList { + gfileList fl; + HANDLE d; + WIN32_FIND_DATA f; + bool_t first; + } NativeFileList; + + static gfileList *NativeFlOpen(const char *path, bool_t dirs) { + NativeFileList *p; + (void) dirs; + + if (!(p = gfxAlloc(sizeof(NativeFileList)))) + return 0; + if ((p->d = FindFirstFile(path, &p->f)) == INVALID_HANDLE_VALUE) { + gfxFree(p); + return 0; + } + p->first = TRUE; + return &p->fl; + } + + static const char *NativeFlRead(gfileList *pfl) { + #define nfl ((NativeFileList *)pfl) + while(1) { + if (!nfl->first && !FindNextFile(nfl->d, &nfl->f)) + return 0; + nfl->first = FALSE; + if (nfl->f.cFileName[0] == '.') + continue; + if (nfl->fl.dirs) { + if ((nfl->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + break; + } else { + if (!(nfl->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + break; + } + } + return nfl->f.cFileName; + #undef nfl + } + + static void NativeFlClose(gfileList *pfl) { + CloseHandle(((NativeFileList *)pfl)->d); + gfxFree(pfl); + } + + #else + #include + + typedef struct NativeFileList { + gfileList fl; + DIR * d; + struct dirent * f; + } NativeFileList; + + static gfileList *NativeFlOpen(const char *path, bool_t dirs) { + NativeFileList *p; + (void) dirs; + + if (!(p = gfxAlloc(sizeof(NativeFileList)))) + return 0; + if (!(p->d = opendir(path))) { + gfxFree(p); + return 0; + } + return &p->fl; + } + + static const char *NativeFlRead(gfileList *pfl) { + #define nfl ((NativeFileList *)pfl) + while(1) { + if (!(nfl->f = readdir(nfl->d))) + return 0; + if (nfl->f->d_name[0] == '.') + continue; + + #ifdef _DIRENT_HAVE_D_TYPE + if (nfl->fl.dirs) { + if (nfl->f->d_type == DT_DIR) + break; + } else { + if (nfl->f->d_type == DT_REG) + break; + } + #else + // Oops - no type field. We could use stat() here but that would mean + // concatting the supplied path to the found filename. + // That all just seems too hard. Instead we just don't + // distinguish between files and directories. + break; + #endif + } + return nfl->f->d_name; + #undef nfl + } + + static void NativeFlClose(gfileList *pfl) { + closedir(((NativeFileList *)pfl)->d); + gfxFree(pfl); + } + #endif +#endif + +#endif //GFX_USE_GFILE && GFILE_NEED_NATIVEFS diff --git a/src/gfile/gfile_fs_petitfs.c b/src/gfile/gfile_fs_petitfs.c new file mode 100644 index 00000000..9b3b510b --- /dev/null +++ b/src/gfile/gfile_fs_petitfs.c @@ -0,0 +1,157 @@ +/* + * 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 + */ + +/******************************************************** + * The PETITFS file-system + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_PETITFS + +#include "gfile_fs.h" +#include "petitfs_wrapper.h" + +static bool_t petitfsExists(const char* fname); +static bool_t petitfsOpen(GFILE* f, const char* fname); +static int petitfsRead(GFILE* f, void* buf, int size); +static bool_t petitfsSetPos(GFILE* f, long int pos); +#if GFILE_NEED_FILELISTS && _FS_MINIMIZE <= 1 + static gfileList *petitfsFlOpen(const char *path, bool_t dirs); + static const char *petitfsFlRead(gfileList *pfl); + static void petitfsFlClose(gfileList *pfl); +#endif + +const GFILEVMT FsPetitFSVMT = { + GFSFLG_WRITEABLE | GFSFLG_SEEKABLE, + 'F', + 0, + petitfsExists, + 0, // No Filesize + 0, + petitfsOpen, + 0, // No Close + petitfsRead, + 0, // No Write + petitfsSetPos, + 0, // No Getsize + 0, // No EOF + 0, 0, 0, // No Mount, UnMount or Sync + #if GFILE_NEED_FILELISTS + #if _USE_DIR + petitfsFlOpen, petitfsFlRead, petitfsFlClose + #else + 0, 0, 0 + #endif + #endif +}; + +// Our directory list structure +typedef struct petitfsList { + gfileList fl; // This must be the first element. + DIR dir; + FILINFO fno; +} petitfsList; + +// optimize these later on. Use an array to have multiple +static bool_t petitfs_mounted = FALSE; +static FATFS petitfs_fs; + +static bool_t petitfsExists(const char* fname) +{ + // Mount first + if (!petitfs_mounted && pf_mount(&petitfs_fs) != FR_OK) + return FALSE; + + // Open + if (pf_open(fname) != FR_OK) + return FALSE; + + return TRUE; +} + +static bool_t petitfsOpen(GFILE* f, const char* fname) +{ + // No writing + if ((f->flags & GFILEFLG_WRITE)) + return FALSE; + + // Mount first + if (!petitfs_mounted && pf_mount(&petitfs_fs) != FR_OK) + return FALSE; + + // Open + if (pf_open(fname) != FR_OK) + return FALSE; + + f->obj = &petitfs_fs; + return TRUE; +} + +static int petitfsRead(GFILE* f, void* buf, int size) +{ + int br; + (void) f; + + if (pf_read(buf, size, (UINT*)&br) != FR_OK) + return 0; + + return br; +} + +static bool_t petitfsSetPos(GFILE* f, long int pos) +{ + (void) f; + return pf_lseek((DWORD)pos) == FR_OK; +} + +#if GFILE_NEED_FILELISTS + static gfileList *petitfsFlOpen(const char *path, bool_t dirs) { + petitfsList *p; + (void) dirs; + + if (!(p = gfxAlloc(sizeof(petitfsList)))) + return 0; + + if (pf_opendir(&p->dir, path) != FR_OK) { + gfxFree(p); + return 0; + } + return &p->fl; + } + + static const char *petitfsFlRead(gfileList *pfl) { + #define ffl ((petitfsList *)pfl) + + while(1) { + // Read the next entry + if (pf_readdir(&ffl->dir, &ffl->fno) != FR_OK || !ffl->fno.fname[0]) + return 0; + + /* Ignore dot entries */ + if (ffl->fno.fname[0] == '.') continue; + + /* Is it a directory */ + if (ffl->fl.dirs) { + if ((ffl->fno.fattrib & AM_DIR)) + break; + } else { + if (!(ffl->fno.fattrib & (AM_DIR|AM_HID|AM_SYS))) + break; + } + } + + return ffl->fno.fname; + } + + static void petitfsFlClose(gfileList *pfl) { + gfxFree(pfl); + } + +#endif + +#endif //GFX_USE_GFILE && GFILE_NEED_PETITFS diff --git a/src/gfile/gfile_fs_ram.c b/src/gfile/gfile_fs_ram.c new file mode 100644 index 00000000..f71eff6c --- /dev/null +++ b/src/gfile/gfile_fs_ram.c @@ -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 + */ + +/******************************************************** + * The RAM file-system + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_RAMFS + +#include "gfile_fs.h" + +#error "GFILE: RAMFS Not implemented yet" + +const GFILEVMT FsRAMVMT = { + 0, // flags + 'R', // prefix + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + #if GFILE_NEED_FILELISTS + 0, 0, 0, + #endif +}; + +#endif //GFX_USE_GFILE && GFILE_NEED_RAMFS diff --git a/src/gfile/gfile_fs_rom.c b/src/gfile/gfile_fs_rom.c new file mode 100644 index 00000000..6b7719c7 --- /dev/null +++ b/src/gfile/gfile_fs_rom.c @@ -0,0 +1,178 @@ +/* + * 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 + */ + +/******************************************************** + * The ROM file-system + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_ROMFS + +#include "gfile_fs.h" + +#include + +// What directory file formats do we understand +#define ROMFS_DIR_VER_MAX 0x0000 + +// Compression Formats +#define ROMFS_CMP_UNCOMPRESSED 0 + +typedef struct ROMFS_DIRENTRY { + uint16_t ver; // Directory Entry Version + uint16_t cmp; // Compression format + const struct ROMFS_DIRENTRY * next; // The next entry + const char * name; // The file name + long int size; // The file size + const char * file; // The file data +} ROMFS_DIRENTRY; + +#define ROMFS_DIRENTRY_HEAD 0 +#include "romfs_files.h" +static const ROMFS_DIRENTRY const *FsROMHead = ROMFS_DIRENTRY_HEAD; + +typedef struct ROMFileList { + gfileList fl; + const ROMFS_DIRENTRY *pdir; +} ROMFileList; + + +static bool_t ROMExists(const char *fname); +static long int ROMFilesize(const char *fname); +static bool_t ROMOpen(GFILE *f, const char *fname); +static void ROMClose(GFILE *f); +static int ROMRead(GFILE *f, void *buf, int size); +static bool_t ROMSetpos(GFILE *f, long int pos); +static long int ROMGetsize(GFILE *f); +static bool_t ROMEof(GFILE *f); +#if GFILE_NEED_FILELISTS + static gfileList *ROMFlOpen(const char *path, bool_t dirs); + static const char *ROMFlRead(gfileList *pfl); + static void ROMFlClose(gfileList *pfl); +#endif + +const GFILEVMT FsROMVMT = { + GFSFLG_CASESENSITIVE|GFSFLG_SEEKABLE|GFSFLG_FAST, // flags + 'S', // prefix + 0, ROMExists, ROMFilesize, 0, + ROMOpen, ROMClose, ROMRead, 0, + ROMSetpos, ROMGetsize, ROMEof, + 0, 0, 0, + #if GFILE_NEED_FILELISTS + ROMFlOpen, ROMFlRead, ROMFlClose + #endif +}; + +static const ROMFS_DIRENTRY *ROMFindFile(const char *fname) +{ + const ROMFS_DIRENTRY *p; + + for(p = FsROMHead; p; p = p->next) { + if (p->ver <= ROMFS_DIR_VER_MAX && p->cmp == ROMFS_CMP_UNCOMPRESSED && !strcmp(p->name, fname)) + break; + } + return p; +} + +static bool_t ROMExists(const char *fname) +{ + return ROMFindFile(fname) != 0; +} + +static long int ROMFilesize(const char *fname) +{ + const ROMFS_DIRENTRY *p; + + if (!(p = ROMFindFile(fname))) return -1; + return p->size; +} + +static bool_t ROMOpen(GFILE *f, const char *fname) +{ + const ROMFS_DIRENTRY *p; + + if (!(p = ROMFindFile(fname))) return FALSE; + f->obj = (void *)p; + return TRUE; +} + +static void ROMClose(GFILE *f) +{ + (void)f; +} + +static int ROMRead(GFILE *f, void *buf, int size) +{ + const ROMFS_DIRENTRY *p; + + p = (const ROMFS_DIRENTRY *)f->obj; + if (p->size - f->pos < size) + size = p->size - f->pos; + if (size <= 0) return 0; + memcpy(buf, p->file+f->pos, size); + return size; +} + +static bool_t ROMSetpos(GFILE *f, long int pos) +{ + return pos <= ((const ROMFS_DIRENTRY *)f->obj)->size; +} + +static long int ROMGetsize(GFILE *f) +{ + return ((const ROMFS_DIRENTRY *)f->obj)->size; +} + +static bool_t ROMEof(GFILE *f) +{ + return f->pos >= ((const ROMFS_DIRENTRY *)f->obj)->size; +} + +#if GFILE_NEED_FILELISTS + static gfileList *ROMFlOpen(const char *path, bool_t dirs) { + ROMFileList * p; + (void) path; + + // We don't support directories or path searching + if (dirs) + return 0; + + // Allocate the list buffer + if (!(p = gfxAlloc(sizeof(ROMFileList)))) + return 0; + + // Initialize it and return it. + p->pdir = 0; + return &p->fl; + } + + static const char *ROMFlRead(gfileList *pfl) { + #define rfl ((ROMFileList *)pfl) + + // Is it the first entry + if (!rfl->pdir) { + rfl->pdir = FsROMHead; + return FsROMHead->name; + } + + // Is it not the last entry + if (rfl->pdir->next) { + rfl->pdir = rfl->pdir->next; + return rfl->pdir->name; + } + + return 0; + #undef rfl + } + + static void ROMFlClose(gfileList *pfl) { + gfxFree(pfl); + } +#endif + +#endif //GFX_USE_GFILE && GFILE_NEED_ROMFS diff --git a/src/gfile/gfile_fs_strings.c b/src/gfile/gfile_fs_strings.c new file mode 100644 index 00000000..35c1575c --- /dev/null +++ b/src/gfile/gfile_fs_strings.c @@ -0,0 +1,138 @@ +/* + * 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 + */ + +/******************************************************** + * The virtual string file + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_STRINGS + +#include "gfile_fs.h" + +#include + +// Special String VMT +static int StringRead(GFILE *f, void *buf, int size) { + int res; + char *p; + + p = ((char *)f->obj) + f->pos; + for(res = 0; res < size && *p; res++, p++, buf = ((char *)buf)+1) + ((char *)buf)[0] = *p; + return res; +} +static int StringWrite(GFILE *f, const void *buf, int size) { + if ((f->flags & GFILEFLG_APPEND)) { + while(((char *)f->obj)[f->pos]) + f->pos++; + } + memcpy(((char *)f->obj)+f->pos, buf, size); + ((char *)f->obj)[f->pos+size] = 0; + return size; +} + +static const GFILEVMT StringVMT = { + 0, // flags + '_', // prefix + 0, 0, 0, 0, + 0, 0, StringRead, StringWrite, + 0, 0, 0, + 0, 0, 0, + #if GFILE_NEED_FILELISTS + 0, 0, 0, + #endif +}; + +static void gfileOpenStringFromStaticGFILE(GFILE *f, char *str) { + if ((f->flags & GFILEFLG_TRUNC)) + str[0] = 0; + f->vmt = &StringVMT; + f->obj = str; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; +} + +GFILE *gfileOpenString(char *str, const char *mode) { + GFILE *f; + + // Get an empty file and set the flags + if (!(f = _gfileFindSlot(mode))) + return 0; + + // File is open - fill in all the details + gfileOpenStringFromStaticGFILE(f, str); + return f; +} + +#if GFILE_NEED_PRINTG + int snprintg(char *buf, int maxlen, const char *fmt, ...) { + int res; + GFILE f; + va_list ap; + + if (maxlen <= 1) { + if (maxlen == 1) { + *buf = 0; + return 0; + } + maxlen += 1; + } + + f.flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; + gfileOpenStringFromStaticGFILE(&f, buf); + + va_start(ap, fmt); + res = vfnprintg(&f, maxlen-1, fmt, ap); + va_end(ap); + return res; + } + int vsnprintg(char *buf, int maxlen, const char *fmt, va_list arg) { + GFILE f; + + if (maxlen <= 1) { + if (maxlen == 1) { + *buf = 0; + return 0; + } + maxlen += 1; + } + + f.flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; + gfileOpenStringFromStaticGFILE(&f, buf); + + return vfnprintg(&f, maxlen-1, fmt, arg); + } +#endif + +#if GFILE_NEED_SCANG + int sscang(const char *buf, const char *fmt, ...) { + int res; + GFILE f; + va_list ap; + + f.flags = GFILEFLG_READ; + gfileOpenStringFromStaticGFILE(&f, (char *)buf); + + va_start(ap, fmt); + res = vfscang(&f, fmt, ap); + va_end(ap); + return res; + } + + int vsscang(const char *buf, const char *fmt, va_list arg) { + GFILE f; + + f.flags = GFILEFLG_READ; + gfileOpenStringFromStaticGFILE(&f, (char *)buf); + + return vfscang(&f, fmt, arg); + } +#endif + +#endif //GFX_USE_GFILE && GFILE_NEED_STRINGS diff --git a/src/gfile/gfile_gfile.c b/src/gfile/gfile_gfile.c new file mode 100644 index 00000000..3547f861 --- /dev/null +++ b/src/gfile/gfile_gfile.c @@ -0,0 +1,407 @@ +/* + * 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_petitfs_diskio_chibios.c b/src/gfile/gfile_petitfs_diskio_chibios.c new file mode 100644 index 00000000..90e709e4 --- /dev/null +++ b/src/gfile/gfile_petitfs_diskio_chibios.c @@ -0,0 +1,88 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* This is a stub disk I/O module that acts as front end of the existing */ +/* 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 + +#include "gfile_petitfs_wrapper.h" + +#include + +#if HAL_USE_MMC_SPI && HAL_USE_SDC +#error "cannot specify both MMC_SPI and SDC drivers" +#endif + +#if HAL_USE_MMC_SPI +extern MMCDriver MMCD1; +#elif HAL_USE_SDC +extern SDCDriver SDCD1; +#else +#error "MMC_SPI or SDC driver must be specified" +#endif + +/*-----------------------------------------------------------------------*/ +/* Initialize a Drive */ + +DSTATUS disk_initialize (void) { +#if HAL_USE_MMC_SPI + /* It is initialized externally, just reads the status.*/ + if (blkGetDriverState(&MMCD1) != BLK_READY) + return STA_NOINIT; +#else + /* It is initialized externally, just reads the status.*/ + if (blkGetDriverState(&SDCD1) != BLK_READY) + return STA_NOINIT; +#endif + // All good + return 0; +} + +/*-----------------------------------------------------------------------*/ +/* Read Part Sector(s) */ + +static BYTE sectBuf[512]; +static DWORD sectpos = -1; + +DRESULT disk_readp ( + BYTE* buff, /* [OUT] Pointer to the read buffer */ + DWORD sector, /* [IN] Sector number */ + UINT offset, /* [IN] Byte offset in the sector to start to read */ + UINT count /* [IN] Number of bytes to read */ + ) { + + if (sector != sectpos) { + #if HAL_USE_MMC_SPI + if (blkGetDriverState(&MMCD1) != BLK_READY) + return RES_NOTRDY; + if (mmcStartSequentialRead(&MMCD1, sector)) + return RES_ERROR; + if (mmcSequentialRead(&MMCD1, sectBuf)) + return RES_ERROR; + if (mmcStopSequentialRead(&MMCD1)) + return RES_ERROR; + #else + if (blkGetDriverState(&SDCD1) != BLK_READY) + return RES_NOTRDY; + if (sdcRead(&SDCD1, sector, sectBuf, 1)) + return RES_ERROR; + #endif + sectpos = sector; + } + memcpy(buff, sectBuf + offset, count); + return RES_OK; +} + +#endif // 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 new file mode 100644 index 00000000..8efc7eb9 --- /dev/null +++ b/src/gfile/gfile_petitfs_wrapper.c @@ -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/gfile/gfile_petitfs_wrapper.c + * @brief GFILE PETITFS wrapper. + * + */ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_PETITFS + +#include "gfile_petitfs_wrapper.h" + +// Include the source we want +#include "3rdparty/petitfs-0.03/src/pff.c" + +#endif // GFX_USE_GFILE && GFILE_NEED_PETITFS diff --git a/src/gfile/gfile_petitfs_wrapper.h b/src/gfile/gfile_petitfs_wrapper.h new file mode 100644 index 00000000..e9080b9e --- /dev/null +++ b/src/gfile/gfile_petitfs_wrapper.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_petitfs_wrapper.h + * @brief GFILE PETITFS wrapper. + * + */ + +#ifndef _PETITFS_WRAPPER +#define _PETITFS_WRAPPER + +// Include the petitfs configuration from the local directory not the original source folder +#include "pffconf.h" + +// Include the petitfs API +#include "3rdparty/petitfs-0.03/src/pff.h" + +// Include the petitfs diskio API +#include "3rdparty/petitfs-0.03/src/diskio.h" + +#endif //_PETITFS_WRAPPER diff --git a/src/gfile/gfile_printg.c b/src/gfile/gfile_printg.c new file mode 100644 index 00000000..33db8bc8 --- /dev/null +++ b/src/gfile/gfile_printg.c @@ -0,0 +1,223 @@ +/* + * 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 + */ + +/******************************************************** + * Printg Routines + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_PRINTG + +#include + +#define MAX_FILLER 11 +#define FLOAT_PRECISION 100000 + +int fnprintg(GFILE *f, int maxlen, const char *fmt, ...) { + int res; + va_list ap; + + va_start(ap, fmt); + res = vfnprintg(f, maxlen, fmt, ap); + va_end(ap); + return res; +} + +static char *ltoa_wd(char *p, long num, unsigned radix, long divisor) { + int i; + char * q; + + if (!divisor) divisor = num; + + q = p + MAX_FILLER; + do { + i = (int)(num % radix); + i += '0'; + if (i > '9') + i += 'A' - '0' - 10; + *--q = i; + num /= radix; + } while ((divisor /= radix) != 0); + + i = (int)(p + MAX_FILLER - q); + do { + *p++ = *q++; + } while (--i); + + return p; +} + +int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg) { + int ret; + char *p, *s, c, filler; + int i, precision, width; + bool_t is_long, left_align; + long l; + #if GFILE_ALLOW_FLOATS + float f; + char tmpbuf[2*MAX_FILLER + 1]; + #else + char tmpbuf[MAX_FILLER + 1]; + #endif + + ret = 0; + if (maxlen < 0) + return 0; + if (!maxlen) + maxlen = -1; + + while (*fmt) { + if (*fmt != '%') { + gfileWrite(f, fmt, 1); + ret++; if (!--maxlen) return ret; + fmt++; + continue; + } + fmt++; + + p = s = tmpbuf; + left_align = FALSE; + filler = ' '; + width = 0; + precision = 0; + + if (*fmt == '-') { + fmt++; + left_align = TRUE; + } + if (*fmt == '.') { + fmt++; + filler = '0'; + } + + while (1) { + c = *fmt++; + if (c >= '0' && c <= '9') { + c -= '0'; + width = width * 10 + c; + } else if (c == '*') + width = va_arg(arg, int); + else + break; + } + if (c == '.') { + while (1) { + c = *fmt++; + if (c >= '0' && c <= '9') { + c -= '0'; + precision = precision * 10 + c; + } else if (c == '*') + precision = va_arg(arg, int); + else + break; + } + } + /* Long modifier.*/ + if (c == 'l' || c == 'L') { + is_long = TRUE; + if (*fmt) + c = *fmt++; + } + else + is_long = (c >= 'A') && (c <= 'Z'); + + /* Command decoding.*/ + switch (c) { + case 0: + return ret; + case 'c': + filler = ' '; + *p++ = va_arg(arg, int); + break; + case 's': + filler = ' '; + if ((s = va_arg(arg, char *)) == 0) + s = "(null)"; + if (precision == 0) + precision = 32767; + for (p = s; *p && (--precision >= 0); p++); + break; + case 'D': + case 'd': + if (is_long) + l = va_arg(arg, long); + else + l = va_arg(arg, int); + if (l < 0) { + *p++ = '-'; + l = -l; + } + p = ltoa_wd(p, l, 10, 0); + break; + #if GFILE_ALLOW_FLOATS + case 'f': + f = (float) va_arg(arg, double); + if (f < 0) { + *p++ = '-'; + f = -f; + } + l = f; + p = ltoa_wd(p, l, 10, 0); + *p++ = '.'; + l = (f - l) * FLOAT_PRECISION; + p = ltoa_wd(p, l, 10, FLOAT_PRECISION / 10); + break; + #endif + case 'X': + case 'x': + c = 16; + goto unsigned_common; + case 'U': + case 'u': + c = 10; + goto unsigned_common; + case 'O': + case 'o': + c = 8; + unsigned_common: + if (is_long) + l = va_arg(arg, long); + else + l = va_arg(arg, int); + p = ltoa_wd(p, l, c, 0); + break; + default: + *p++ = c; + break; + } + + i = (int)(p - s); + if ((width -= i) < 0) + width = 0; + if (left_align == FALSE) + width = -width; + if (width < 0) { + if (*s == '-' && filler == '0') { + gfileWrite(f, s++, 1); + ret++; if (!--maxlen) return ret; + i--; + } + do { + gfileWrite(f, &filler, 1); + ret++; if (!--maxlen) return ret; + } while (++width != 0); + } + while (--i >= 0) { + gfileWrite(f, s++, 1); + ret++; if (!--maxlen) return ret; + } + while (width) { + gfileWrite(f, &filler, 1); + ret++; if (!--maxlen) return ret; + width--; + } + } + return ret; +} + +#endif //GFX_USE_GFILE && GFILE_NEED_PRINTG diff --git a/src/gfile/gfile_scang.c b/src/gfile/gfile_scang.c new file mode 100644 index 00000000..5fac5ce3 --- /dev/null +++ b/src/gfile/gfile_scang.c @@ -0,0 +1,234 @@ +/* + * 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 + */ + +/******************************************************** + * Scang Routines + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_SCANG + +int fscang(GFILE *f, const char *fmt, ...) { + int res; + va_list ap; + + va_start(ap, fmt); + res = vfscang(f, fmt, ap); + va_end(ap); + return res; +} + +int vfscang(GFILE *f, const char *fmt, va_list arg) { + int res, width, size, base; + unsigned long num; + char c; + bool_t assign, negate; + char *p; + + for(res = 0; *fmt; fmt++) { + switch(*fmt) { + case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': + break; + + case '%': + fmt++; + assign = TRUE; + negate = FALSE; + width = 0; + size = 1; + num = 0; + + if (*fmt == '*') { + fmt++; + assign = FALSE; + } + while(*fmt >= '0' && *fmt <= '9') + width = width * 10 + (*fmt++ - '0'); + if (*fmt == 'h') { + fmt++; + size = 0; + } else if (*fmt == 'l') { + fmt++; + size = 2; + } else if (*fmt == 'L') { + fmt++; + size = 3; + } + switch(*fmt) { + case 0: + return res; + case '%': + goto matchchar; + case 'c': + if (!width) { + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + width = 1; + } else { + if (!gfileRead(f, &c, 1)) return res; + } + if (assign) { + p = va_arg(arg, char *); + res++; + *p++ = c; + } + while(--width) { + if (!gfileRead(f, &c, 1)) return res; + if (assign) *p++ = c; + } + break; + case 's': + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (assign) { + p = va_arg(arg, char *); + res++; + *p++ = c; + } + if (width) { + while(--width) { + if (!gfileRead(f, &c, 1)) { + if (assign) *((char *)p) = 0; + return res; + } + if (assign) *p++ = c; + } + } else { + while(1) { + if (!gfileRead(f, &c, 1)) { + if (assign) *((char *)p) = 0; + return res; + } + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': break; + default: + if (assign) *p++ = c; + continue; + } + break; + } + //ungetch(c); + } + if (assign) *p = 0; + break; + case 'd': base = 10; goto getnum; + case 'i': base = -1; goto getnum; + case 'o': base = 8; goto getnum; + case 'u': base = 10; goto getnum; + case 'x': base = 16; goto getnum; + case 'b': base = 2; + getnum: + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (c == '-' && *fmt != 'u') { + negate = TRUE; + if ((width && !--width) || !gfileRead(f, &c, 1)) return res; + } + if (base == -1) { + if (c == '0') { + if ((width && !--width) || !gfileRead(f, &c, 1)) goto assignnum; + switch(c) { + case 'x': case 'X': + base = 16; + if ((width && !--width) || !gfileRead(f, &c, 1)) return res; + break; + case 'b': case 'B': + base = 2; + if ((width && !--width) || !gfileRead(f, &c, 1)) return res; + break; + default: + base = 8; + break; + } + } else + base = 10; + } + while(1) { + if (c >= '0' && c <= '9' && c - '0' < base) + num = num * base + (c - '0'); + else if (c >= 'A' && c <= 'F' && base == 16) + num = num * base + (c - ('A'-10)); + else if (c >= 'a' && c <= 'f' && base == 16) + num = num * base + (c - ('a'-10)); + else { + // ungetch(c) + break; + } + if ((width && !--width) || !gfileRead(f, &c, 1)) + break; + } + + assignnum: + if (negate) + num = -num; + + if (assign) { + switch(size) { + case 0: // short + p = (char *)va_arg(arg, short *); + res++; + *((short *)p) = (short)num; + case 1: // int + p = (char *)va_arg(arg, int *); + res++; + *((int *)p) = (int)num; + case 2: case 3: // long + p = (char *)va_arg(arg, long *); + res++; + *((long *)p) = (long)num; + } + } + break; + + #if GFILE_ALLOW_FLOATS + case 'e': case 'f': case 'g': + // TODO + #endif + default: + return res; + } + + break; + + default: + matchchar: + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (c != *fmt) return res; + break; + } + } + return res; +} + +#endif //GFX_USE_GFILE && GFILE_NEED_SCANG diff --git a/src/gfile/gfile_stdio.c b/src/gfile/gfile_stdio.c new file mode 100644 index 00000000..20169ef2 --- /dev/null +++ b/src/gfile/gfile_stdio.c @@ -0,0 +1,47 @@ +/* + * 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 + */ + +/******************************************************** + * Stdio Emulation Routines + ********************************************************/ + +#include "gfx.h" + +#if GFX_USE_GFILE && GFILE_NEED_STDIO + +size_t gstdioRead(void * ptr, size_t size, size_t count, FILE *f) { + return gfileRead(f, ptr, size*count)/size; +} + +size_t gstdioWrite(const void * ptr, size_t size, size_t count, FILE *f) { + return gfileWrite(f, ptr, size*count)/size; +} + +int gstdioSeek(FILE *f, size_t offset, int origin) { + switch(origin) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += f->pos; + break; + case SEEK_END: + offset += gfileGetSize(f); + break; + default: + return -1; + } + return gfileSetPos(f, offset) ? 0 : -1; +} + +int gstdioGetpos(FILE *f, long int *pos) { + if (!(f->flags & GFILEFLG_OPEN)) + return -1; + *pos = f->pos; + return 0; +} + +#endif //GFX_USE_GFILE && GFILE_NEED_STDIO diff --git a/src/gfile/inc_chibiosfs.c b/src/gfile/inc_chibiosfs.c deleted file mode 100644 index 72826e28..00000000 --- a/src/gfile/inc_chibiosfs.c +++ /dev/null @@ -1,68 +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 - */ - -/******************************************************** - * The ChibiOS BaseFileStream file-system - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS - -#include "gfile_fs.h" - -static void ChibiOSBFSClose(GFILE *f); -static int ChibiOSBFSRead(GFILE *f, void *buf, int size); -static int ChibiOSBFSWrite(GFILE *f, const void *buf, int size); -static bool_t ChibiOSBFSSetpos(GFILE *f, long int pos); -static long int ChibiOSBFSGetsize(GFILE *f); -static bool_t ChibiOSBFSEof(GFILE *f); - -static const GFILEVMT FsCHIBIOSVMT = { - GFSFLG_SEEKABLE|GFSFLG_WRITEABLE, // flags - 0, // prefix - 0, 0, 0, 0, - 0, ChibiOSBFSClose, ChibiOSBFSRead, ChibiOSBFSWrite, - ChibiOSBFSSetpos, ChibiOSBFSGetsize, ChibiOSBFSEof, - 0, 0, 0, - #if GFILE_NEED_FILELISTS - 0, 0, 0, - #endif -}; - -static void ChibiOSBFSClose(GFILE *f) { - chFileStreamClose(((BaseFileStream *)f->obj)); -} -static int ChibiOSBFSRead(GFILE *f, void *buf, int size) { - return chSequentialStreamRead(((BaseFileStream *)f->obj), (uint8_t *)buf, size); -} -static int ChibiOSBFSWrite(GFILE *f, const void *buf, int size) { - return chSequentialStreamWrite(((BaseFileStream *)f->obj), (uint8_t *)buf, size); -} -static bool_t ChibiOSBFSSetpos(GFILE *f, long int pos) { - chFileStreamSeek(((BaseFileStream *)f->obj), pos); - return TRUE; -} -static long int ChibiOSBFSGetsize(GFILE *f) { return chFileStreamGetSize(((BaseFileStream *)f->obj)); } -static bool_t ChibiOSBFSEof(GFILE *f) { return f->pos >= chFileStreamGetSize(((BaseFileStream *)f->obj)); } - -GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode) { - GFILE * f; - - // Get an empty file and set the flags - if (!(f = _gfileFindSlot(mode))) - return 0; - - // File is open - fill in all the details - f->vmt = &FsCHIBIOSVMT; - f->obj = BaseFileStreamPtr; - f->pos = 0; - f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; - return f; -} - -#endif //GFX_USE_GFILE && GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS diff --git a/src/gfile/inc_fatfs.c b/src/gfile/inc_fatfs.c deleted file mode 100644 index 2e4ef52b..00000000 --- a/src/gfile/inc_fatfs.c +++ /dev/null @@ -1,325 +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 - */ - -/******************************************************** - * The FATFS file-system - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_FATFS - -#include "gfile_fs.h" -#include "fatfs_wrapper.h" - -/******************************************************** - * The FAT file-system VMT - ********************************************************/ - -static bool_t fatfsDel(const char* fname); -static bool_t fatfsExists(const char* fname); -static long int fatfsFileSize(const char* fname); -static bool_t fatfsRename(const char* oldname, const char* newname); -static bool_t fatfsOpen(GFILE* f, const char* fname); -static void fatfsClose(GFILE* f); -static int fatfsRead(GFILE* f, void* buf, int size); -static int fatfsWrite(GFILE* f, const void* buf, int size); -static bool_t fatfsSetPos(GFILE* f, long int pos); -static long int fatfsGetSize(GFILE* f); -static bool_t fatfsEOF(GFILE* f); -static bool_t fatfsMount(const char* drive); -static bool_t fatfsUnmount(const char* drive); -static bool_t fatfsSync(GFILE* f); -#if GFILE_NEED_FILELISTS && _FS_MINIMIZE <= 1 - static gfileList *fatfsFlOpen(const char *path, bool_t dirs); - static const char *fatfsFlRead(gfileList *pfl); - static void fatfsFlClose(gfileList *pfl); -#endif - -const GFILEVMT FsFatFSVMT = { - GFSFLG_WRITEABLE | GFSFLG_SEEKABLE, - 'F', - fatfsDel, - fatfsExists, - fatfsFileSize, - fatfsRename, - fatfsOpen, - fatfsClose, - fatfsRead, - fatfsWrite, - fatfsSetPos, - fatfsGetSize, - fatfsEOF, - fatfsMount, fatfsUnmount, fatfsSync, - #if GFILE_NEED_FILELISTS - #if _FS_MINIMIZE <= 1 - fatfsFlOpen, fatfsFlRead, fatfsFlClose - #else - 0, 0, 0 - #endif - #endif -}; - -// Our directory list structure -typedef struct fatfsList { - gfileList fl; // This must be the first element. - DIR dir; - FILINFO fno; - #if _USE_LFN - char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */ - #endif -} fatfsList; - -// optimize these later on. Use an array to have multiple FatFS -static bool_t fatfs_mounted = FALSE; -static FATFS fatfs_fs; - -static BYTE fatfs_flags2mode(GFILE* f) -{ - BYTE mode = 0; - - if (f->flags & GFILEFLG_READ) - mode |= FA_READ; - if (f->flags & GFILEFLG_WRITE) - mode |= FA_WRITE; - if (f->flags & GFILEFLG_APPEND) - mode |= 0; // ToDo - if (f->flags & GFILEFLG_TRUNC) - mode |= FA_CREATE_ALWAYS; - - /* ToDo - Complete */ - return mode; -} - -static bool_t fatfsDel(const char* fname) -{ - FRESULT ferr; - - ferr = f_unlink( (const TCHAR*)fname ); - if (ferr != FR_OK) - return FALSE; - - return TRUE; -} - -static bool_t fatfsExists(const char* fname) -{ - FRESULT ferr; - FILINFO fno; - - ferr = f_stat( (const TCHAR*)fname, &fno); - if (ferr != FR_OK) - return FALSE; - - return TRUE; -} - -static long int fatfsFileSize(const char* fname) -{ - FRESULT ferr; - FILINFO fno; - - ferr = f_stat( (const TCHAR*)fname, &fno ); - if (ferr != FR_OK) - return 0; - - return (long int)fno.fsize; -} - -static bool_t fatfsRename(const char* oldname, const char* newname) -{ - FRESULT ferr; - - ferr = f_rename( (const TCHAR*)oldname, (const TCHAR*)newname ); - if (ferr != FR_OK) - return FALSE; - - return TRUE; -} - -static bool_t fatfsOpen(GFILE* f, const char* fname) -{ - FIL* fd; - - #if !GFILE_NEED_NOAUTOMOUNT - if (!fatfs_mounted && !fatfsMount("")) - return FALSE; - #endif - - if (!(fd = gfxAlloc(sizeof(FIL)))) - return FALSE; - - if (f_open(fd, fname, fatfs_flags2mode(f)) != FR_OK) { - gfxFree(fd); - f->obj = 0; - - return FALSE; - } - - f->obj = (void*)fd; - - #if !GFILE_NEED_NOAUTOSYNC - // no need to sync when not opening for write - if (f->flags & GFILEFLG_WRITE) { - f_sync( (FIL*)f->obj ); - } - #endif - - return TRUE; -} - -static void fatfsClose(GFILE* f) -{ - if ((FIL*)f->obj != 0) { - f_close( (FIL*)f->obj ); - gfxFree( (FIL*)f->obj ); - } -} - -static int fatfsRead(GFILE* f, void* buf, int size) -{ - int br; - - f_read( (FIL*)f->obj, buf, size, (UINT*)&br); - - return br; -} - -static int fatfsWrite(GFILE* f, const void* buf, int size) -{ - int wr; - - f_write( (FIL*)f->obj, buf, size, (UINT*)&wr); - #if !GFILE_NEED_NOAUTOSYNC - f_sync( (FIL*)f->obj ); - #endif - - return wr; -} - -static bool_t fatfsSetPos(GFILE* f, long int pos) -{ - FRESULT ferr; - - ferr = f_lseek( (FIL*)f->obj, (DWORD)pos ); - if (ferr != FR_OK) - return FALSE; - - return TRUE; -} - -static long int fatfsGetSize(GFILE* f) -{ - return (long int)f_size( (FIL*)f->obj ); -} - -static bool_t fatfsEOF(GFILE* f) -{ - if ( f_eof( (FIL*)f->obj ) != 0) - return TRUE; - else - return FALSE; -} - -static bool_t fatfsMount(const char* drive) -{ - FRESULT ferr; - - if (!fatfs_mounted) { - ferr = f_mount(&fatfs_fs, drive, 1); - if (ferr != FR_OK) - return FALSE; - fatfs_mounted = TRUE; - return TRUE; - } - - return FALSE; -} - -static bool_t fatfsUnmount(const char* drive) -{ - (void)drive; - - if (fatfs_mounted) { - // FatFS does not provide an unmount routine. - fatfs_mounted = FALSE; - return TRUE; - } - - return FALSE; -} - -static bool_t fatfsSync(GFILE *f) -{ - FRESULT ferr; - - ferr = f_sync( (FIL*)f->obj ); - if (ferr != FR_OK) { - return FALSE; - } - - return TRUE; -} - -#if GFILE_NEED_FILELISTS && _FS_MINIMIZE <= 1 - static gfileList *fatfsFlOpen(const char *path, bool_t dirs) { - fatfsList *p; - (void) dirs; - - if (!(p = gfxAlloc(sizeof(fatfsList)))) - return 0; - - if (f_opendir(&p->dir, path) != FR_OK) { - gfxFree(p); - return 0; - } - return &p->fl; - } - - static const char *fatfsFlRead(gfileList *pfl) { - #define ffl ((fatfsList *)pfl) - - while(1) { - #if _USE_LFN - ffl->fno.lfname = ffl->lfn; - ffl->fno.lfsize = sizeof(ffl->lfn); - #endif - - // Read the next entry - if (f_readdir(&ffl->dir, &ffl->fno) != FR_OK || !ffl->fno.fname[0]) - return 0; - - /* Ignore dot entries */ - if (ffl->fno.fname[0] == '.') continue; - - /* Is it a directory */ - if (ffl->fl.dirs) { - if ((ffl->fno.fattrib & AM_DIR)) - break; - } else { - if (!(ffl->fno.fattrib & AM_DIR)) - break; - } - } - - #if _USE_LFN - return ffl->fno.lfname[0] ? ffl->fno.lfname : ffl->fno.fname; - #else - return ffl->fno.fname; - #endif - #undef ffl - } - - static void fatfsFlClose(gfileList *pfl) { - f_closedir(&((fatfsList *)pfl)->dir); - gfxFree(pfl); - } - -#endif - -#endif //GFX_USE_GFILE && GFILE_NEED_FATFS - diff --git a/src/gfile/inc_memfs.c b/src/gfile/inc_memfs.c deleted file mode 100644 index 150220b8..00000000 --- a/src/gfile/inc_memfs.c +++ /dev/null @@ -1,65 +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 - */ - -/******************************************************** - * The virtual memory file-system - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_MEMFS - -#include "gfile_fs.h" - -#include - -static int MEMRead(GFILE *f, void *buf, int size); -static int MEMWrite(GFILE *f, const void *buf, int size); -static bool_t MEMSetpos(GFILE *f, long int pos); - -static const GFILEVMT FsMemVMT = { - GFSFLG_SEEKABLE|GFSFLG_WRITEABLE, // flags - 0, // prefix - 0, 0, 0, 0, - 0, 0, MEMRead, MEMWrite, - MEMSetpos, 0, 0, - 0, 0, 0, - #if GFILE_NEED_FILELISTS - 0, 0, 0, - #endif -}; - -static int MEMRead(GFILE *f, void *buf, int size) { - memcpy(buf, ((char *)f->obj)+f->pos, size); - return size; -} -static int MEMWrite(GFILE *f, const void *buf, int size) { - memcpy(((char *)f->obj)+f->pos, buf, size); - return size; -} -static bool_t MEMSetpos(GFILE *f, long int pos) { - (void) f; - (void) pos; - return TRUE; -} - -GFILE * gfileOpenMemory(void *memptr, const char *mode) { - GFILE *f; - - // Get an empty file and set the flags - if (!(f = _gfileFindSlot(mode))) - return 0; - - // File is open - fill in all the details - f->vmt = &FsMemVMT; - f->obj = memptr; - f->pos = 0; - f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; - return f; -} - -#endif //GFX_USE_GFILE && GFILE_NEED_MEMFS diff --git a/src/gfile/inc_nativefs.c b/src/gfile/inc_nativefs.c deleted file mode 100644 index b8441ac7..00000000 --- a/src/gfile/inc_nativefs.c +++ /dev/null @@ -1,235 +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 - */ - -/******************************************************** - * The native file-system - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_NATIVEFS - -#include "gfile_fs.h" - -#include -#include -#include -//#include - -static bool_t NativeDel(const char *fname); -static bool_t NativeExists(const char *fname); -static long int NativeFilesize(const char *fname); -static bool_t NativeRen(const char *oldname, const char *newname); -static bool_t NativeOpen(GFILE *f, const char *fname); -static void NativeClose(GFILE *f); -static int NativeRead(GFILE *f, void *buf, int size); -static int NativeWrite(GFILE *f, const void *buf, int size); -static bool_t NativeSetpos(GFILE *f, long int pos); -static long int NativeGetsize(GFILE *f); -static bool_t NativeEof(GFILE *f); -#if GFILE_NEED_FILELISTS - static gfileList *NativeFlOpen(const char *path, bool_t dirs); - static const char *NativeFlRead(gfileList *pfl); - static void NativeFlClose(gfileList *pfl); -#endif - -const GFILEVMT FsNativeVMT = { - #if defined(WIN32) || GFX_USE_OS_WIN32 - GFSFLG_TEXTMODES| - #else - GFSFLG_CASESENSITIVE| - #endif - GFSFLG_WRITEABLE|GFSFLG_SEEKABLE|GFSFLG_FAST, // flags - 'N', // prefix - NativeDel, NativeExists, NativeFilesize, NativeRen, - NativeOpen, NativeClose, NativeRead, NativeWrite, - NativeSetpos, NativeGetsize, NativeEof, - 0, 0, 0, - #if GFILE_NEED_FILELISTS - NativeFlOpen, NativeFlRead, NativeFlClose - #endif -}; - -void _gfileNativeAssignStdio(void) { - static GFILE NativeStdIn; - static GFILE NativeStdOut; - static GFILE NativeStdErr; - - NativeStdIn.flags = GFILEFLG_OPEN|GFILEFLG_READ; - NativeStdIn.vmt = &FsNativeVMT; - NativeStdIn.obj = (void *)stdin; - NativeStdIn.pos = 0; - gfileStdIn = &NativeStdIn; - NativeStdOut.flags = GFILEFLG_OPEN|GFILEFLG_WRITE|GFILEFLG_APPEND; - NativeStdOut.vmt = &FsNativeVMT; - NativeStdOut.obj = (void *)stdout; - NativeStdOut.pos = 0; - gfileStdOut = &NativeStdOut; - NativeStdErr.flags = GFILEFLG_OPEN|GFILEFLG_WRITE|GFILEFLG_APPEND; - NativeStdErr.vmt = &FsNativeVMT; - NativeStdErr.obj = (void *)stderr; - NativeStdErr.pos = 0; - gfileStdErr = &NativeStdErr; -} - -static void Native_flags2mode(char *buf, uint16_t flags) { - if (flags & GFILEFLG_MUSTEXIST) - *buf = 'r'; - else if (flags & GFILEFLG_APPEND) - *buf = 'a'; - else - *buf = 'w'; - buf++; - if ((flags & (GFILEFLG_READ|GFILEFLG_WRITE)) == (GFILEFLG_READ|GFILEFLG_WRITE)) - *buf++ = '+'; - if (flags & GFILEFLG_BINARY) - *buf++ = 'b'; - if (flags & GFILEFLG_MUSTNOTEXIST) - *buf++ = 'x'; - *buf++ = 0; -} - -static bool_t NativeDel(const char *fname) { return remove(fname) ? FALSE : TRUE; } -static void NativeClose(GFILE *f) { fclose((FILE *)f->obj); } -static int NativeRead(GFILE *f, void *buf, int size) { return fread(buf, 1, size, (FILE *)f->obj); } -static int NativeWrite(GFILE *f, const void *buf, int size) { return fwrite(buf, 1, size, (FILE *)f->obj); } -static bool_t NativeSetpos(GFILE *f, long int pos) { return fseek((FILE *)f->obj, pos, SEEK_SET) ? FALSE : TRUE; } -static bool_t NativeEof(GFILE *f) { return feof((FILE *)f->obj) ? TRUE : FALSE; } -static bool_t NativeRen(const char *oldname, const char *newname) { return rename(oldname, newname) ? FALSE : TRUE; } -static bool_t NativeExists(const char *fname) { - // We define access this way so we don't have to include which may - // (and does under windows) contain conflicting definitions for types such as uint16_t. - extern int access(const char *pathname, int mode); - return access(fname, 0) ? FALSE : TRUE; -} -static long int NativeFilesize(const char *fname) { - struct stat st; - if (stat(fname, &st)) return -1; - return st.st_size; -} -static bool_t NativeOpen(GFILE *f, const char *fname) { - FILE *fd; - char mode[5]; - - Native_flags2mode(mode, f->flags); - if (!(fd = fopen(fname, mode))) - return FALSE; - f->obj = (void *)fd; - return TRUE; -} -static long int NativeGetsize(GFILE *f) { - struct stat st; - if (fstat(fileno((FILE *)f->obj), &st)) return -1; - return st.st_size; -} - -#if GFILE_NEED_FILELISTS - #if defined(WIN32) || GFX_USE_OS_WIN32 - typedef struct NativeFileList { - gfileList fl; - HANDLE d; - WIN32_FIND_DATA f; - bool_t first; - } NativeFileList; - - static gfileList *NativeFlOpen(const char *path, bool_t dirs) { - NativeFileList *p; - (void) dirs; - - if (!(p = gfxAlloc(sizeof(NativeFileList)))) - return 0; - if ((p->d = FindFirstFile(path, &p->f)) == INVALID_HANDLE_VALUE) { - gfxFree(p); - return 0; - } - p->first = TRUE; - return &p->fl; - } - - static const char *NativeFlRead(gfileList *pfl) { - #define nfl ((NativeFileList *)pfl) - while(1) { - if (!nfl->first && !FindNextFile(nfl->d, &nfl->f)) - return 0; - nfl->first = FALSE; - if (nfl->f.cFileName[0] == '.') - continue; - if (nfl->fl.dirs) { - if ((nfl->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - break; - } else { - if (!(nfl->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - break; - } - } - return nfl->f.cFileName; - #undef nfl - } - - static void NativeFlClose(gfileList *pfl) { - CloseHandle(((NativeFileList *)pfl)->d); - gfxFree(pfl); - } - - #else - #include - - typedef struct NativeFileList { - gfileList fl; - DIR * d; - struct dirent * f; - } NativeFileList; - - static gfileList *NativeFlOpen(const char *path, bool_t dirs) { - NativeFileList *p; - (void) dirs; - - if (!(p = gfxAlloc(sizeof(NativeFileList)))) - return 0; - if (!(p->d = opendir(path))) { - gfxFree(p); - return 0; - } - return &p->fl; - } - - static const char *NativeFlRead(gfileList *pfl) { - #define nfl ((NativeFileList *)pfl) - while(1) { - if (!(nfl->f = readdir(nfl->d))) - return 0; - if (nfl->f->d_name[0] == '.') - continue; - - #ifdef _DIRENT_HAVE_D_TYPE - if (nfl->fl.dirs) { - if (nfl->f->d_type == DT_DIR) - break; - } else { - if (nfl->f->d_type == DT_REG) - break; - } - #else - // Oops - no type field. We could use stat() here but that would mean - // concatting the supplied path to the found filename. - // That all just seems too hard. Instead we just don't - // distinguish between files and directories. - break; - #endif - } - return nfl->f->d_name; - #undef nfl - } - - static void NativeFlClose(gfileList *pfl) { - closedir(((NativeFileList *)pfl)->d); - gfxFree(pfl); - } - #endif -#endif - -#endif //GFX_USE_GFILE && GFILE_NEED_NATIVEFS diff --git a/src/gfile/inc_petitfs.c b/src/gfile/inc_petitfs.c deleted file mode 100644 index 9b3b510b..00000000 --- a/src/gfile/inc_petitfs.c +++ /dev/null @@ -1,157 +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 - */ - -/******************************************************** - * The PETITFS file-system - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_PETITFS - -#include "gfile_fs.h" -#include "petitfs_wrapper.h" - -static bool_t petitfsExists(const char* fname); -static bool_t petitfsOpen(GFILE* f, const char* fname); -static int petitfsRead(GFILE* f, void* buf, int size); -static bool_t petitfsSetPos(GFILE* f, long int pos); -#if GFILE_NEED_FILELISTS && _FS_MINIMIZE <= 1 - static gfileList *petitfsFlOpen(const char *path, bool_t dirs); - static const char *petitfsFlRead(gfileList *pfl); - static void petitfsFlClose(gfileList *pfl); -#endif - -const GFILEVMT FsPetitFSVMT = { - GFSFLG_WRITEABLE | GFSFLG_SEEKABLE, - 'F', - 0, - petitfsExists, - 0, // No Filesize - 0, - petitfsOpen, - 0, // No Close - petitfsRead, - 0, // No Write - petitfsSetPos, - 0, // No Getsize - 0, // No EOF - 0, 0, 0, // No Mount, UnMount or Sync - #if GFILE_NEED_FILELISTS - #if _USE_DIR - petitfsFlOpen, petitfsFlRead, petitfsFlClose - #else - 0, 0, 0 - #endif - #endif -}; - -// Our directory list structure -typedef struct petitfsList { - gfileList fl; // This must be the first element. - DIR dir; - FILINFO fno; -} petitfsList; - -// optimize these later on. Use an array to have multiple -static bool_t petitfs_mounted = FALSE; -static FATFS petitfs_fs; - -static bool_t petitfsExists(const char* fname) -{ - // Mount first - if (!petitfs_mounted && pf_mount(&petitfs_fs) != FR_OK) - return FALSE; - - // Open - if (pf_open(fname) != FR_OK) - return FALSE; - - return TRUE; -} - -static bool_t petitfsOpen(GFILE* f, const char* fname) -{ - // No writing - if ((f->flags & GFILEFLG_WRITE)) - return FALSE; - - // Mount first - if (!petitfs_mounted && pf_mount(&petitfs_fs) != FR_OK) - return FALSE; - - // Open - if (pf_open(fname) != FR_OK) - return FALSE; - - f->obj = &petitfs_fs; - return TRUE; -} - -static int petitfsRead(GFILE* f, void* buf, int size) -{ - int br; - (void) f; - - if (pf_read(buf, size, (UINT*)&br) != FR_OK) - return 0; - - return br; -} - -static bool_t petitfsSetPos(GFILE* f, long int pos) -{ - (void) f; - return pf_lseek((DWORD)pos) == FR_OK; -} - -#if GFILE_NEED_FILELISTS - static gfileList *petitfsFlOpen(const char *path, bool_t dirs) { - petitfsList *p; - (void) dirs; - - if (!(p = gfxAlloc(sizeof(petitfsList)))) - return 0; - - if (pf_opendir(&p->dir, path) != FR_OK) { - gfxFree(p); - return 0; - } - return &p->fl; - } - - static const char *petitfsFlRead(gfileList *pfl) { - #define ffl ((petitfsList *)pfl) - - while(1) { - // Read the next entry - if (pf_readdir(&ffl->dir, &ffl->fno) != FR_OK || !ffl->fno.fname[0]) - return 0; - - /* Ignore dot entries */ - if (ffl->fno.fname[0] == '.') continue; - - /* Is it a directory */ - if (ffl->fl.dirs) { - if ((ffl->fno.fattrib & AM_DIR)) - break; - } else { - if (!(ffl->fno.fattrib & (AM_DIR|AM_HID|AM_SYS))) - break; - } - } - - return ffl->fno.fname; - } - - static void petitfsFlClose(gfileList *pfl) { - gfxFree(pfl); - } - -#endif - -#endif //GFX_USE_GFILE && GFILE_NEED_PETITFS diff --git a/src/gfile/inc_printg.c b/src/gfile/inc_printg.c deleted file mode 100644 index 33db8bc8..00000000 --- a/src/gfile/inc_printg.c +++ /dev/null @@ -1,223 +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 - */ - -/******************************************************** - * Printg Routines - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_PRINTG - -#include - -#define MAX_FILLER 11 -#define FLOAT_PRECISION 100000 - -int fnprintg(GFILE *f, int maxlen, const char *fmt, ...) { - int res; - va_list ap; - - va_start(ap, fmt); - res = vfnprintg(f, maxlen, fmt, ap); - va_end(ap); - return res; -} - -static char *ltoa_wd(char *p, long num, unsigned radix, long divisor) { - int i; - char * q; - - if (!divisor) divisor = num; - - q = p + MAX_FILLER; - do { - i = (int)(num % radix); - i += '0'; - if (i > '9') - i += 'A' - '0' - 10; - *--q = i; - num /= radix; - } while ((divisor /= radix) != 0); - - i = (int)(p + MAX_FILLER - q); - do { - *p++ = *q++; - } while (--i); - - return p; -} - -int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg) { - int ret; - char *p, *s, c, filler; - int i, precision, width; - bool_t is_long, left_align; - long l; - #if GFILE_ALLOW_FLOATS - float f; - char tmpbuf[2*MAX_FILLER + 1]; - #else - char tmpbuf[MAX_FILLER + 1]; - #endif - - ret = 0; - if (maxlen < 0) - return 0; - if (!maxlen) - maxlen = -1; - - while (*fmt) { - if (*fmt != '%') { - gfileWrite(f, fmt, 1); - ret++; if (!--maxlen) return ret; - fmt++; - continue; - } - fmt++; - - p = s = tmpbuf; - left_align = FALSE; - filler = ' '; - width = 0; - precision = 0; - - if (*fmt == '-') { - fmt++; - left_align = TRUE; - } - if (*fmt == '.') { - fmt++; - filler = '0'; - } - - while (1) { - c = *fmt++; - if (c >= '0' && c <= '9') { - c -= '0'; - width = width * 10 + c; - } else if (c == '*') - width = va_arg(arg, int); - else - break; - } - if (c == '.') { - while (1) { - c = *fmt++; - if (c >= '0' && c <= '9') { - c -= '0'; - precision = precision * 10 + c; - } else if (c == '*') - precision = va_arg(arg, int); - else - break; - } - } - /* Long modifier.*/ - if (c == 'l' || c == 'L') { - is_long = TRUE; - if (*fmt) - c = *fmt++; - } - else - is_long = (c >= 'A') && (c <= 'Z'); - - /* Command decoding.*/ - switch (c) { - case 0: - return ret; - case 'c': - filler = ' '; - *p++ = va_arg(arg, int); - break; - case 's': - filler = ' '; - if ((s = va_arg(arg, char *)) == 0) - s = "(null)"; - if (precision == 0) - precision = 32767; - for (p = s; *p && (--precision >= 0); p++); - break; - case 'D': - case 'd': - if (is_long) - l = va_arg(arg, long); - else - l = va_arg(arg, int); - if (l < 0) { - *p++ = '-'; - l = -l; - } - p = ltoa_wd(p, l, 10, 0); - break; - #if GFILE_ALLOW_FLOATS - case 'f': - f = (float) va_arg(arg, double); - if (f < 0) { - *p++ = '-'; - f = -f; - } - l = f; - p = ltoa_wd(p, l, 10, 0); - *p++ = '.'; - l = (f - l) * FLOAT_PRECISION; - p = ltoa_wd(p, l, 10, FLOAT_PRECISION / 10); - break; - #endif - case 'X': - case 'x': - c = 16; - goto unsigned_common; - case 'U': - case 'u': - c = 10; - goto unsigned_common; - case 'O': - case 'o': - c = 8; - unsigned_common: - if (is_long) - l = va_arg(arg, long); - else - l = va_arg(arg, int); - p = ltoa_wd(p, l, c, 0); - break; - default: - *p++ = c; - break; - } - - i = (int)(p - s); - if ((width -= i) < 0) - width = 0; - if (left_align == FALSE) - width = -width; - if (width < 0) { - if (*s == '-' && filler == '0') { - gfileWrite(f, s++, 1); - ret++; if (!--maxlen) return ret; - i--; - } - do { - gfileWrite(f, &filler, 1); - ret++; if (!--maxlen) return ret; - } while (++width != 0); - } - while (--i >= 0) { - gfileWrite(f, s++, 1); - ret++; if (!--maxlen) return ret; - } - while (width) { - gfileWrite(f, &filler, 1); - ret++; if (!--maxlen) return ret; - width--; - } - } - return ret; -} - -#endif //GFX_USE_GFILE && GFILE_NEED_PRINTG diff --git a/src/gfile/inc_ramfs.c b/src/gfile/inc_ramfs.c deleted file mode 100644 index f71eff6c..00000000 --- a/src/gfile/inc_ramfs.c +++ /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 - */ - -/******************************************************** - * The RAM file-system - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_RAMFS - -#include "gfile_fs.h" - -#error "GFILE: RAMFS Not implemented yet" - -const GFILEVMT FsRAMVMT = { - 0, // flags - 'R', // prefix - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - #if GFILE_NEED_FILELISTS - 0, 0, 0, - #endif -}; - -#endif //GFX_USE_GFILE && GFILE_NEED_RAMFS diff --git a/src/gfile/inc_romfs.c b/src/gfile/inc_romfs.c deleted file mode 100644 index 6b7719c7..00000000 --- a/src/gfile/inc_romfs.c +++ /dev/null @@ -1,178 +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 - */ - -/******************************************************** - * The ROM file-system - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_ROMFS - -#include "gfile_fs.h" - -#include - -// What directory file formats do we understand -#define ROMFS_DIR_VER_MAX 0x0000 - -// Compression Formats -#define ROMFS_CMP_UNCOMPRESSED 0 - -typedef struct ROMFS_DIRENTRY { - uint16_t ver; // Directory Entry Version - uint16_t cmp; // Compression format - const struct ROMFS_DIRENTRY * next; // The next entry - const char * name; // The file name - long int size; // The file size - const char * file; // The file data -} ROMFS_DIRENTRY; - -#define ROMFS_DIRENTRY_HEAD 0 -#include "romfs_files.h" -static const ROMFS_DIRENTRY const *FsROMHead = ROMFS_DIRENTRY_HEAD; - -typedef struct ROMFileList { - gfileList fl; - const ROMFS_DIRENTRY *pdir; -} ROMFileList; - - -static bool_t ROMExists(const char *fname); -static long int ROMFilesize(const char *fname); -static bool_t ROMOpen(GFILE *f, const char *fname); -static void ROMClose(GFILE *f); -static int ROMRead(GFILE *f, void *buf, int size); -static bool_t ROMSetpos(GFILE *f, long int pos); -static long int ROMGetsize(GFILE *f); -static bool_t ROMEof(GFILE *f); -#if GFILE_NEED_FILELISTS - static gfileList *ROMFlOpen(const char *path, bool_t dirs); - static const char *ROMFlRead(gfileList *pfl); - static void ROMFlClose(gfileList *pfl); -#endif - -const GFILEVMT FsROMVMT = { - GFSFLG_CASESENSITIVE|GFSFLG_SEEKABLE|GFSFLG_FAST, // flags - 'S', // prefix - 0, ROMExists, ROMFilesize, 0, - ROMOpen, ROMClose, ROMRead, 0, - ROMSetpos, ROMGetsize, ROMEof, - 0, 0, 0, - #if GFILE_NEED_FILELISTS - ROMFlOpen, ROMFlRead, ROMFlClose - #endif -}; - -static const ROMFS_DIRENTRY *ROMFindFile(const char *fname) -{ - const ROMFS_DIRENTRY *p; - - for(p = FsROMHead; p; p = p->next) { - if (p->ver <= ROMFS_DIR_VER_MAX && p->cmp == ROMFS_CMP_UNCOMPRESSED && !strcmp(p->name, fname)) - break; - } - return p; -} - -static bool_t ROMExists(const char *fname) -{ - return ROMFindFile(fname) != 0; -} - -static long int ROMFilesize(const char *fname) -{ - const ROMFS_DIRENTRY *p; - - if (!(p = ROMFindFile(fname))) return -1; - return p->size; -} - -static bool_t ROMOpen(GFILE *f, const char *fname) -{ - const ROMFS_DIRENTRY *p; - - if (!(p = ROMFindFile(fname))) return FALSE; - f->obj = (void *)p; - return TRUE; -} - -static void ROMClose(GFILE *f) -{ - (void)f; -} - -static int ROMRead(GFILE *f, void *buf, int size) -{ - const ROMFS_DIRENTRY *p; - - p = (const ROMFS_DIRENTRY *)f->obj; - if (p->size - f->pos < size) - size = p->size - f->pos; - if (size <= 0) return 0; - memcpy(buf, p->file+f->pos, size); - return size; -} - -static bool_t ROMSetpos(GFILE *f, long int pos) -{ - return pos <= ((const ROMFS_DIRENTRY *)f->obj)->size; -} - -static long int ROMGetsize(GFILE *f) -{ - return ((const ROMFS_DIRENTRY *)f->obj)->size; -} - -static bool_t ROMEof(GFILE *f) -{ - return f->pos >= ((const ROMFS_DIRENTRY *)f->obj)->size; -} - -#if GFILE_NEED_FILELISTS - static gfileList *ROMFlOpen(const char *path, bool_t dirs) { - ROMFileList * p; - (void) path; - - // We don't support directories or path searching - if (dirs) - return 0; - - // Allocate the list buffer - if (!(p = gfxAlloc(sizeof(ROMFileList)))) - return 0; - - // Initialize it and return it. - p->pdir = 0; - return &p->fl; - } - - static const char *ROMFlRead(gfileList *pfl) { - #define rfl ((ROMFileList *)pfl) - - // Is it the first entry - if (!rfl->pdir) { - rfl->pdir = FsROMHead; - return FsROMHead->name; - } - - // Is it not the last entry - if (rfl->pdir->next) { - rfl->pdir = rfl->pdir->next; - return rfl->pdir->name; - } - - return 0; - #undef rfl - } - - static void ROMFlClose(gfileList *pfl) { - gfxFree(pfl); - } -#endif - -#endif //GFX_USE_GFILE && GFILE_NEED_ROMFS diff --git a/src/gfile/inc_scang.c b/src/gfile/inc_scang.c deleted file mode 100644 index 5fac5ce3..00000000 --- a/src/gfile/inc_scang.c +++ /dev/null @@ -1,234 +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 - */ - -/******************************************************** - * Scang Routines - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_SCANG - -int fscang(GFILE *f, const char *fmt, ...) { - int res; - va_list ap; - - va_start(ap, fmt); - res = vfscang(f, fmt, ap); - va_end(ap); - return res; -} - -int vfscang(GFILE *f, const char *fmt, va_list arg) { - int res, width, size, base; - unsigned long num; - char c; - bool_t assign, negate; - char *p; - - for(res = 0; *fmt; fmt++) { - switch(*fmt) { - case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': - break; - - case '%': - fmt++; - assign = TRUE; - negate = FALSE; - width = 0; - size = 1; - num = 0; - - if (*fmt == '*') { - fmt++; - assign = FALSE; - } - while(*fmt >= '0' && *fmt <= '9') - width = width * 10 + (*fmt++ - '0'); - if (*fmt == 'h') { - fmt++; - size = 0; - } else if (*fmt == 'l') { - fmt++; - size = 2; - } else if (*fmt == 'L') { - fmt++; - size = 3; - } - switch(*fmt) { - case 0: - return res; - case '%': - goto matchchar; - case 'c': - if (!width) { - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - width = 1; - } else { - if (!gfileRead(f, &c, 1)) return res; - } - if (assign) { - p = va_arg(arg, char *); - res++; - *p++ = c; - } - while(--width) { - if (!gfileRead(f, &c, 1)) return res; - if (assign) *p++ = c; - } - break; - case 's': - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - if (assign) { - p = va_arg(arg, char *); - res++; - *p++ = c; - } - if (width) { - while(--width) { - if (!gfileRead(f, &c, 1)) { - if (assign) *((char *)p) = 0; - return res; - } - if (assign) *p++ = c; - } - } else { - while(1) { - if (!gfileRead(f, &c, 1)) { - if (assign) *((char *)p) = 0; - return res; - } - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': break; - default: - if (assign) *p++ = c; - continue; - } - break; - } - //ungetch(c); - } - if (assign) *p = 0; - break; - case 'd': base = 10; goto getnum; - case 'i': base = -1; goto getnum; - case 'o': base = 8; goto getnum; - case 'u': base = 10; goto getnum; - case 'x': base = 16; goto getnum; - case 'b': base = 2; - getnum: - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - if (c == '-' && *fmt != 'u') { - negate = TRUE; - if ((width && !--width) || !gfileRead(f, &c, 1)) return res; - } - if (base == -1) { - if (c == '0') { - if ((width && !--width) || !gfileRead(f, &c, 1)) goto assignnum; - switch(c) { - case 'x': case 'X': - base = 16; - if ((width && !--width) || !gfileRead(f, &c, 1)) return res; - break; - case 'b': case 'B': - base = 2; - if ((width && !--width) || !gfileRead(f, &c, 1)) return res; - break; - default: - base = 8; - break; - } - } else - base = 10; - } - while(1) { - if (c >= '0' && c <= '9' && c - '0' < base) - num = num * base + (c - '0'); - else if (c >= 'A' && c <= 'F' && base == 16) - num = num * base + (c - ('A'-10)); - else if (c >= 'a' && c <= 'f' && base == 16) - num = num * base + (c - ('a'-10)); - else { - // ungetch(c) - break; - } - if ((width && !--width) || !gfileRead(f, &c, 1)) - break; - } - - assignnum: - if (negate) - num = -num; - - if (assign) { - switch(size) { - case 0: // short - p = (char *)va_arg(arg, short *); - res++; - *((short *)p) = (short)num; - case 1: // int - p = (char *)va_arg(arg, int *); - res++; - *((int *)p) = (int)num; - case 2: case 3: // long - p = (char *)va_arg(arg, long *); - res++; - *((long *)p) = (long)num; - } - } - break; - - #if GFILE_ALLOW_FLOATS - case 'e': case 'f': case 'g': - // TODO - #endif - default: - return res; - } - - break; - - default: - matchchar: - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - if (c != *fmt) return res; - break; - } - } - return res; -} - -#endif //GFX_USE_GFILE && GFILE_NEED_SCANG diff --git a/src/gfile/inc_stdio.c b/src/gfile/inc_stdio.c deleted file mode 100644 index 20169ef2..00000000 --- a/src/gfile/inc_stdio.c +++ /dev/null @@ -1,47 +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 - */ - -/******************************************************** - * Stdio Emulation Routines - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_STDIO - -size_t gstdioRead(void * ptr, size_t size, size_t count, FILE *f) { - return gfileRead(f, ptr, size*count)/size; -} - -size_t gstdioWrite(const void * ptr, size_t size, size_t count, FILE *f) { - return gfileWrite(f, ptr, size*count)/size; -} - -int gstdioSeek(FILE *f, size_t offset, int origin) { - switch(origin) { - case SEEK_SET: - break; - case SEEK_CUR: - offset += f->pos; - break; - case SEEK_END: - offset += gfileGetSize(f); - break; - default: - return -1; - } - return gfileSetPos(f, offset) ? 0 : -1; -} - -int gstdioGetpos(FILE *f, long int *pos) { - if (!(f->flags & GFILEFLG_OPEN)) - return -1; - *pos = f->pos; - return 0; -} - -#endif //GFX_USE_GFILE && GFILE_NEED_STDIO diff --git a/src/gfile/inc_strings.c b/src/gfile/inc_strings.c deleted file mode 100644 index 35c1575c..00000000 --- a/src/gfile/inc_strings.c +++ /dev/null @@ -1,138 +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 - */ - -/******************************************************** - * The virtual string file - ********************************************************/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_STRINGS - -#include "gfile_fs.h" - -#include - -// Special String VMT -static int StringRead(GFILE *f, void *buf, int size) { - int res; - char *p; - - p = ((char *)f->obj) + f->pos; - for(res = 0; res < size && *p; res++, p++, buf = ((char *)buf)+1) - ((char *)buf)[0] = *p; - return res; -} -static int StringWrite(GFILE *f, const void *buf, int size) { - if ((f->flags & GFILEFLG_APPEND)) { - while(((char *)f->obj)[f->pos]) - f->pos++; - } - memcpy(((char *)f->obj)+f->pos, buf, size); - ((char *)f->obj)[f->pos+size] = 0; - return size; -} - -static const GFILEVMT StringVMT = { - 0, // flags - '_', // prefix - 0, 0, 0, 0, - 0, 0, StringRead, StringWrite, - 0, 0, 0, - 0, 0, 0, - #if GFILE_NEED_FILELISTS - 0, 0, 0, - #endif -}; - -static void gfileOpenStringFromStaticGFILE(GFILE *f, char *str) { - if ((f->flags & GFILEFLG_TRUNC)) - str[0] = 0; - f->vmt = &StringVMT; - f->obj = str; - f->pos = 0; - f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; -} - -GFILE *gfileOpenString(char *str, const char *mode) { - GFILE *f; - - // Get an empty file and set the flags - if (!(f = _gfileFindSlot(mode))) - return 0; - - // File is open - fill in all the details - gfileOpenStringFromStaticGFILE(f, str); - return f; -} - -#if GFILE_NEED_PRINTG - int snprintg(char *buf, int maxlen, const char *fmt, ...) { - int res; - GFILE f; - va_list ap; - - if (maxlen <= 1) { - if (maxlen == 1) { - *buf = 0; - return 0; - } - maxlen += 1; - } - - f.flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; - gfileOpenStringFromStaticGFILE(&f, buf); - - va_start(ap, fmt); - res = vfnprintg(&f, maxlen-1, fmt, ap); - va_end(ap); - return res; - } - int vsnprintg(char *buf, int maxlen, const char *fmt, va_list arg) { - GFILE f; - - if (maxlen <= 1) { - if (maxlen == 1) { - *buf = 0; - return 0; - } - maxlen += 1; - } - - f.flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; - gfileOpenStringFromStaticGFILE(&f, buf); - - return vfnprintg(&f, maxlen-1, fmt, arg); - } -#endif - -#if GFILE_NEED_SCANG - int sscang(const char *buf, const char *fmt, ...) { - int res; - GFILE f; - va_list ap; - - f.flags = GFILEFLG_READ; - gfileOpenStringFromStaticGFILE(&f, (char *)buf); - - va_start(ap, fmt); - res = vfscang(&f, fmt, ap); - va_end(ap); - return res; - } - - int vsscang(const char *buf, const char *fmt, va_list arg) { - GFILE f; - - f.flags = GFILEFLG_READ; - gfileOpenStringFromStaticGFILE(&f, (char *)buf); - - return vfscang(&f, fmt, arg); - } -#endif - -#endif //GFX_USE_GFILE && GFILE_NEED_STRINGS diff --git a/src/gfile/petitfs_chibios_diskio.c b/src/gfile/petitfs_chibios_diskio.c deleted file mode 100644 index dda3f6fc..00000000 --- a/src/gfile/petitfs_chibios_diskio.c +++ /dev/null @@ -1,82 +0,0 @@ -/*-----------------------------------------------------------------------*/ -/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ -/*-----------------------------------------------------------------------*/ -/* This is a stub disk I/O module that acts as front end of the existing */ -/* disk I/O modules and attach it to FatFs module with common interface. */ -/*-----------------------------------------------------------------------*/ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_PETITFS && GFX_USE_OS_CHIBIOS - -#include "petitfs_wrapper.h" - -#include - -#if HAL_USE_MMC_SPI && HAL_USE_SDC -#error "cannot specify both MMC_SPI and SDC drivers" -#endif - -#if HAL_USE_MMC_SPI -extern MMCDriver MMCD1; -#elif HAL_USE_SDC -extern SDCDriver SDCD1; -#else -#error "MMC_SPI or SDC driver must be specified" -#endif - -/*-----------------------------------------------------------------------*/ -/* Initialize a Drive */ - -DSTATUS disk_initialize (void) { -#if HAL_USE_MMC_SPI - /* It is initialized externally, just reads the status.*/ - if (blkGetDriverState(&MMCD1) != BLK_READY) - return STA_NOINIT; -#else - /* It is initialized externally, just reads the status.*/ - if (blkGetDriverState(&SDCD1) != BLK_READY) - return STA_NOINIT; -#endif - // All good - return 0; -} - -/*-----------------------------------------------------------------------*/ -/* Read Part Sector(s) */ - -static BYTE sectBuf[512]; -static DWORD sectpos = -1; - -DRESULT disk_readp ( - BYTE* buff, /* [OUT] Pointer to the read buffer */ - DWORD sector, /* [IN] Sector number */ - UINT offset, /* [IN] Byte offset in the sector to start to read */ - UINT count /* [IN] Number of bytes to read */ - ) { - - if (sector != sectpos) { - #if HAL_USE_MMC_SPI - if (blkGetDriverState(&MMCD1) != BLK_READY) - return RES_NOTRDY; - if (mmcStartSequentialRead(&MMCD1, sector)) - return RES_ERROR; - if (mmcSequentialRead(&MMCD1, sectBuf)) - return RES_ERROR; - if (mmcStopSequentialRead(&MMCD1)) - return RES_ERROR; - #else - if (blkGetDriverState(&SDCD1) != BLK_READY) - return RES_NOTRDY; - if (sdcRead(&SDCD1, sector, sectBuf, 1)) - return RES_ERROR; - #endif - sectpos = sector; - } - memcpy(buff, sectBuf + offset, count); - return RES_OK; -} - -#endif // GFX_USE_GFILE && GFILE_NEED_PETITFS && GFX_USE_OS_CHIBIOS - - diff --git a/src/gfile/petitfs_wrapper.c b/src/gfile/petitfs_wrapper.c deleted file mode 100644 index 183edb70..00000000 --- a/src/gfile/petitfs_wrapper.c +++ /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/gfile/petitfs_wrapper.c - * @brief GFILE PETITFS wrapper. - * - */ - -#include "gfx.h" - -#if GFX_USE_GFILE && GFILE_NEED_PETITFS - -#include "petitfs_wrapper.h" - -// Include the source we want -#include "3rdparty/petitfs-0.03/src/pff.c" - -#endif // GFX_USE_GFILE && GFILE_NEED_PETITFS diff --git a/src/gfile/petitfs_wrapper.h b/src/gfile/petitfs_wrapper.h deleted file mode 100644 index 4f91bd7f..00000000 --- a/src/gfile/petitfs_wrapper.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/petitfs_wrapper.h - * @brief GFILE PETITFS wrapper. - * - */ - -#ifndef _PETITFS_WRAPPER -#define _PETITFS_WRAPPER - -// Include the petitfs configuration from the local directory not the original source folder -#include "pffconf.h" - -// Include the petitfs API -#include "3rdparty/petitfs-0.03/src/pff.h" - -// Include the petitfs diskio API -#include "3rdparty/petitfs-0.03/src/diskio.h" - -#endif //_PETITFS_WRAPPER diff --git a/src/gfile/sys_make.mk b/src/gfile/sys_make.mk index e14527c6..0a85d1ae 100644 --- a/src/gfile/sys_make.mk +++ b/src/gfile/sys_make.mk @@ -1,18 +1,18 @@ -GFXSRC += $(GFXLIB)/src/gfile/gfile.c \ - $(GFXLIB)/src/gfile/inc_nativefs.c \ - $(GFXLIB)/src/gfile/inc_ramfs.c \ - $(GFXLIB)/src/gfile/inc_romfs.c \ - $(GFXLIB)/src/gfile/inc_fatfs.c \ - $(GFXLIB)/src/gfile/inc_petitfs.c \ - $(GFXLIB)/src/gfile/inc_memfs.c \ - $(GFXLIB)/src/gfile/inc_chibiosfs.c \ - $(GFXLIB)/src/gfile/inc_strings.c \ - $(GFXLIB)/src/gfile/inc_printg.c \ - $(GFXLIB)/src/gfile/inc_scang.c \ - $(GFXLIB)/src/gfile/inc_stdio.c \ - $(GFXLIB)/src/gfile/fatfs_wrapper.c \ - $(GFXLIB)/src/gfile/fatfs_chibios_diskio.c \ - $(GFXLIB)/src/gfile/petitfs_wrapper.c \ - $(GFXLIB)/src/gfile/petitfs_chibios_diskio.c \ +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/ginput/dial.c b/src/ginput/dial.c deleted file mode 100644 index 410efc1a..00000000 --- a/src/ginput/dial.c +++ /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/ginput/dial.c - * @brief GINPUT dial code. - * - * @defgroup Dial Dial - * @ingroup GINPUT - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GINPUT && GINPUT_NEED_DIAL - -#include "src/ginput/driver_dial.h" - -static GTIMER_DECL(DialTimer); -static struct DialStatus_t { - uint16_t sensitivity; - uint16_t lastvalue; - uint16_t max; -} DialStatus[GINPUT_DIAL_NUM_PORTS]; - -// The reading callback function -static void DialCallback(uint16_t instance, uint16_t rawvalue) { - struct DialStatus_t *pds; - GSourceListener *psl; - GEventDial *pe; - - /* Get the information we need */ - pds = DialStatus+instance; - - /* Range scale - if needed */ - if (pds->max != GINPUT_DIAL_MAX_VALUE) - rawvalue = (uint16_t)((uint32_t)rawvalue * pds->max / GINPUT_DIAL_MAX_VALUE); - - /* Forget about changes below our sensitivity threshold */ - if (rawvalue >= pds->lastvalue) { - if (rawvalue - pds->lastvalue < pds->sensitivity) return; - } else { - if (pds->lastvalue - rawvalue < pds->sensitivity) return; - } - - /* Save the value */ - pds->lastvalue = rawvalue; - - // Send the event to the listeners that are interested. - psl = 0; - while ((psl = geventGetSourceListener((GSourceHandle)(DialStatus+instance), psl))) { - if (!(pe = (GEventDial *)geventGetEventBuffer(psl))) - continue; - pe->type = GEVENT_DIAL; - pe->instance = instance; - pe->value = pds->lastvalue; - pe->maxvalue = pds->max; - geventSendEvent(psl); - } -} - -GSourceHandle ginputGetDial(uint16_t instance) { - struct DialStatus_t *pds; - - if (instance >= GINPUT_DIAL_NUM_PORTS) - return 0; - - // Do we need to initialise the dial subsystem? - if (!gtimerIsActive(&DialTimer)) { - for(pds = DialStatus; pds < DialStatus+GINPUT_DIAL_NUM_PORTS; pds++) { - pds->max = GINPUT_DIAL_MAX_VALUE; -#if GINPUT_DIAL_MAX_VALUE < 100 - pds->sensitivity = 1; -#else - pds->sensitivity = GINPUT_DIAL_MAX_VALUE/100; -#endif - pds->lastvalue = 0; - } - ginput_lld_dial_init(); - gtimerStart(&DialTimer, (GTimerFunction)ginput_lld_dial_poll, DialCallback, TRUE, GINPUT_DIAL_POLL_PERIOD); - } - - // OK - return this input - return (GSourceHandle)(DialStatus+instance); -} - -void ginputResetDialRange(uint16_t instance) { - if (instance >= GINPUT_DIAL_NUM_PORTS) - return; - - ginputSetDialRange(instance, GINPUT_DIAL_MAX_VALUE); -} - -uint16_t ginputGetDialRange(uint16_t instance) { - if (instance >= GINPUT_DIAL_NUM_PORTS) - return 0; - - return DialStatus[instance].max; -} - -void ginputSetDialRange(uint16_t instance, uint16_t max) { - struct DialStatus_t *pds; - - if (instance >= GINPUT_DIAL_NUM_PORTS) - return; - - pds = DialStatus+instance; - - // Rescale the last value and the sensitivity - if (max != pds->max) { - pds->lastvalue = (uint16_t)((uint32_t)pds->lastvalue * max / pds->max); - pds->sensitivity = (uint16_t)((uint32_t)pds->sensitivity * max / pds->max); - pds->max = max; - } -} - -/** - * @brief Set the level change required before a dial even is generated (threshold) - * @note This is done after range scaling - * - * @param[in] instance The ID of the dial input instance - * @param[in] diff The amount of level changes - */ -void ginputSetDialSensitivity(uint16_t instance, uint16_t diff) { - if (instance >= GINPUT_DIAL_NUM_PORTS) - return; - - DialStatus[instance].sensitivity = diff; -} - -/** - * @brief Get the current dial status - * - * @param[in] instance The ID of the dial input instance - * @param[in] pdial The dial event struct - * - * @return Returns FALSE on an error (eg invalid instance) - */ -bool_t ginputGetDialStatus(uint16_t instance, GEventDial *pdial) { - if (instance >= GINPUT_DIAL_NUM_PORTS) - return FALSE; - pdial->type = GEVENT_DIAL; - pdial->instance = instance; - pdial->value = DialStatus[instance].lastvalue; - pdial->maxvalue = DialStatus[instance].max; - return TRUE; -} - -#endif /* GFX_USE_GINPUT && GINPUT_NEED_DIAL */ -/** @} */ diff --git a/src/ginput/dial.h b/src/ginput/dial.h deleted file mode 100644 index f2d3fb58..00000000 --- a/src/ginput/dial.h +++ /dev/null @@ -1,111 +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/dial.h - * @brief GINPUT GFX User Input subsystem header file. - * - * @defgroup Dial Dial - * @ingroup GINPUT - * - * @details A dial provides a powerful way to navigate through menus - * on a display. - * - * @pre GFX_USE_GINPUT must be set to TRUE in your gfxconf.h - * @pre GINPUT_NEED_DIAL must be set to TRUE in your gfxconf.h - * - * @{ - */ -#ifndef _GINPUT_DIAL_H -#define _GINPUT_DIAL_H - -#if GINPUT_NEED_DIAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -// Event types for various ginput sources -#define GEVENT_DIAL (GEVENT_GINPUT_FIRST+4) - -typedef struct GEventDial_t { - GEventType type; // The type of this event (GEVENT_DIAL) - uint16_t instance; // The dial instance - uint16_t value; // The dial value - uint16_t maxvalue; // The maximum dial value - } GEventDial; - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - - /** - * @brief Create a dial input instance - * - * @param[in] instance The ID of the dial input instance (from 0 to 9999) - * - * @return The soure handle of the created dial instance - */ - GSourceHandle ginputGetDial(uint16_t instance); - - /** - * @brief Reset the value back to the hardware default - * - * @param[in] instance The ID of the dial input instance - */ - void ginputResetDialRange(uint16_t instance); - - /** - * @brief Get the maximum value - * @details The readings are scaled to be 0 ... max. - * - * @param[in] instance The ID of the dial input instance - * - * @return The maximum value - */ - uint16_t ginputGetDialRange(uint16_t instance); - - /** - * @brief Set the maximum value - * @details The readings are scaled to be 0 ... max. - * - * @param[in] instance The ID of the dial input instance - * @param[in] max The maximum value to be set - */ - void ginputSetDialRange(uint16_t instance, uint16_t max); - - /** - * @brief Set the level change required before a dial even is generated (threshold) - * @note This is done after range scaling - * - * @param[in] instance The ID of the dial input instance - * @param[in] diff The amount of level changes - */ - void ginputSetDialSensitivity(uint16_t instance, uint16_t diff); - - /** - * @brief Get the current dial status - * - * @param[in] instance The ID of the dial input instance - * @param[in] pdial The dial event struct - * - * @return Returns FALSE on an error (eg invalid instance) - */ - bool_t ginputGetDialStatus(uint16_t instance, GEventDial *pdial); - -#ifdef __cplusplus -} -#endif - -#endif /* GINPUT_NEED_DIAL */ - -#endif /* _GINPUT_DIAL_H */ -/** @} */ diff --git a/src/ginput/ginput.c b/src/ginput/ginput.c deleted file mode 100644 index abd16cd8..00000000 --- a/src/ginput/ginput.c +++ /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/ginput/ginput.c - * @brief GINPUT subsystem common code. - * - * @addtogroup GINPUT - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GINPUT - -void _ginputInit(void) -{ - /* ToDo */ - - /** - * This should really call an init routine for each ginput sub-system. - * Maybe we'll do this later. - */ -} - -void _ginputDeinit(void) -{ - -} - -#endif /* GFX_USE_GINPUT */ -/** @} */ - diff --git a/src/ginput/ginput_dial.c b/src/ginput/ginput_dial.c new file mode 100644 index 00000000..6af89b31 --- /dev/null +++ b/src/ginput/ginput_dial.c @@ -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/ginput/ginput_dial.c + * @brief GINPUT dial code. + * + * @defgroup Dial Dial + * @ingroup GINPUT + * @{ + */ +#include "gfx.h" + +#if GFX_USE_GINPUT && GINPUT_NEED_DIAL + +#include "driver_dial.h" + +static GTIMER_DECL(DialTimer); +static struct DialStatus_t { + uint16_t sensitivity; + uint16_t lastvalue; + uint16_t max; +} DialStatus[GINPUT_DIAL_NUM_PORTS]; + +// The reading callback function +static void DialCallback(uint16_t instance, uint16_t rawvalue) { + struct DialStatus_t *pds; + GSourceListener *psl; + GEventDial *pe; + + /* Get the information we need */ + pds = DialStatus+instance; + + /* Range scale - if needed */ + if (pds->max != GINPUT_DIAL_MAX_VALUE) + rawvalue = (uint16_t)((uint32_t)rawvalue * pds->max / GINPUT_DIAL_MAX_VALUE); + + /* Forget about changes below our sensitivity threshold */ + if (rawvalue >= pds->lastvalue) { + if (rawvalue - pds->lastvalue < pds->sensitivity) return; + } else { + if (pds->lastvalue - rawvalue < pds->sensitivity) return; + } + + /* Save the value */ + pds->lastvalue = rawvalue; + + // Send the event to the listeners that are interested. + psl = 0; + while ((psl = geventGetSourceListener((GSourceHandle)(DialStatus+instance), psl))) { + if (!(pe = (GEventDial *)geventGetEventBuffer(psl))) + continue; + pe->type = GEVENT_DIAL; + pe->instance = instance; + pe->value = pds->lastvalue; + pe->maxvalue = pds->max; + geventSendEvent(psl); + } +} + +GSourceHandle ginputGetDial(uint16_t instance) { + struct DialStatus_t *pds; + + if (instance >= GINPUT_DIAL_NUM_PORTS) + return 0; + + // Do we need to initialise the dial subsystem? + if (!gtimerIsActive(&DialTimer)) { + for(pds = DialStatus; pds < DialStatus+GINPUT_DIAL_NUM_PORTS; pds++) { + pds->max = GINPUT_DIAL_MAX_VALUE; +#if GINPUT_DIAL_MAX_VALUE < 100 + pds->sensitivity = 1; +#else + pds->sensitivity = GINPUT_DIAL_MAX_VALUE/100; +#endif + pds->lastvalue = 0; + } + ginput_lld_dial_init(); + gtimerStart(&DialTimer, (GTimerFunction)ginput_lld_dial_poll, DialCallback, TRUE, GINPUT_DIAL_POLL_PERIOD); + } + + // OK - return this input + return (GSourceHandle)(DialStatus+instance); +} + +void ginputResetDialRange(uint16_t instance) { + if (instance >= GINPUT_DIAL_NUM_PORTS) + return; + + ginputSetDialRange(instance, GINPUT_DIAL_MAX_VALUE); +} + +uint16_t ginputGetDialRange(uint16_t instance) { + if (instance >= GINPUT_DIAL_NUM_PORTS) + return 0; + + return DialStatus[instance].max; +} + +void ginputSetDialRange(uint16_t instance, uint16_t max) { + struct DialStatus_t *pds; + + if (instance >= GINPUT_DIAL_NUM_PORTS) + return; + + pds = DialStatus+instance; + + // Rescale the last value and the sensitivity + if (max != pds->max) { + pds->lastvalue = (uint16_t)((uint32_t)pds->lastvalue * max / pds->max); + pds->sensitivity = (uint16_t)((uint32_t)pds->sensitivity * max / pds->max); + pds->max = max; + } +} + +/** + * @brief Set the level change required before a dial even is generated (threshold) + * @note This is done after range scaling + * + * @param[in] instance The ID of the dial input instance + * @param[in] diff The amount of level changes + */ +void ginputSetDialSensitivity(uint16_t instance, uint16_t diff) { + if (instance >= GINPUT_DIAL_NUM_PORTS) + return; + + DialStatus[instance].sensitivity = diff; +} + +/** + * @brief Get the current dial status + * + * @param[in] instance The ID of the dial input instance + * @param[in] pdial The dial event struct + * + * @return Returns FALSE on an error (eg invalid instance) + */ +bool_t ginputGetDialStatus(uint16_t instance, GEventDial *pdial) { + if (instance >= GINPUT_DIAL_NUM_PORTS) + return FALSE; + pdial->type = GEVENT_DIAL; + pdial->instance = instance; + pdial->value = DialStatus[instance].lastvalue; + pdial->maxvalue = DialStatus[instance].max; + return TRUE; +} + +#endif /* GFX_USE_GINPUT && GINPUT_NEED_DIAL */ +/** @} */ diff --git a/src/ginput/ginput_dial.h b/src/ginput/ginput_dial.h new file mode 100644 index 00000000..88a2e165 --- /dev/null +++ b/src/ginput/ginput_dial.h @@ -0,0 +1,111 @@ +/* + * 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_dial.h + * @brief GINPUT GFX User Input subsystem header file. + * + * @defgroup Dial Dial + * @ingroup GINPUT + * + * @details A dial provides a powerful way to navigate through menus + * on a display. + * + * @pre GFX_USE_GINPUT must be set to TRUE in your gfxconf.h + * @pre GINPUT_NEED_DIAL must be set to TRUE in your gfxconf.h + * + * @{ + */ +#ifndef _GINPUT_DIAL_H +#define _GINPUT_DIAL_H + +#if GINPUT_NEED_DIAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +// Event types for various ginput sources +#define GEVENT_DIAL (GEVENT_GINPUT_FIRST+4) + +typedef struct GEventDial_t { + GEventType type; // The type of this event (GEVENT_DIAL) + uint16_t instance; // The dial instance + uint16_t value; // The dial value + uint16_t maxvalue; // The maximum dial value + } GEventDial; + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Create a dial input instance + * + * @param[in] instance The ID of the dial input instance (from 0 to 9999) + * + * @return The soure handle of the created dial instance + */ + GSourceHandle ginputGetDial(uint16_t instance); + + /** + * @brief Reset the value back to the hardware default + * + * @param[in] instance The ID of the dial input instance + */ + void ginputResetDialRange(uint16_t instance); + + /** + * @brief Get the maximum value + * @details The readings are scaled to be 0 ... max. + * + * @param[in] instance The ID of the dial input instance + * + * @return The maximum value + */ + uint16_t ginputGetDialRange(uint16_t instance); + + /** + * @brief Set the maximum value + * @details The readings are scaled to be 0 ... max. + * + * @param[in] instance The ID of the dial input instance + * @param[in] max The maximum value to be set + */ + void ginputSetDialRange(uint16_t instance, uint16_t max); + + /** + * @brief Set the level change required before a dial even is generated (threshold) + * @note This is done after range scaling + * + * @param[in] instance The ID of the dial input instance + * @param[in] diff The amount of level changes + */ + void ginputSetDialSensitivity(uint16_t instance, uint16_t diff); + + /** + * @brief Get the current dial status + * + * @param[in] instance The ID of the dial input instance + * @param[in] pdial The dial event struct + * + * @return Returns FALSE on an error (eg invalid instance) + */ + bool_t ginputGetDialStatus(uint16_t instance, GEventDial *pdial); + +#ifdef __cplusplus +} +#endif + +#endif /* GINPUT_NEED_DIAL */ + +#endif /* _GINPUT_DIAL_H */ +/** @} */ diff --git a/src/ginput/ginput_ginput.c b/src/ginput/ginput_ginput.c new file mode 100644 index 00000000..4197fa25 --- /dev/null +++ b/src/ginput/ginput_ginput.c @@ -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/ginput/ginput_ginput.c + * @brief GINPUT subsystem common code. + * + * @addtogroup GINPUT + * @{ + */ +#include "gfx.h" + +#if GFX_USE_GINPUT + +void _ginputInit(void) +{ + /* ToDo */ + + /** + * This should really call an init routine for each ginput sub-system. + * Maybe we'll do this later. + */ +} + +void _ginputDeinit(void) +{ + +} + +#endif /* GFX_USE_GINPUT */ +/** @} */ + diff --git a/src/ginput/ginput_keyboard.c b/src/ginput/ginput_keyboard.c new file mode 100644 index 00000000..5443572b --- /dev/null +++ b/src/ginput/ginput_keyboard.c @@ -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/ginput/ginput_keyboard.c + * @brief GINPUT keyboard code. + * + * @defgroup Keyboard Keyboard + * @ingroup GINPUT + * + * @{ + */ + +#include "gfx.h" + +#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || defined(__DOXYGEN__) + #error "GINPUT: GINPUT_NEED_KEYBOARD - Not Implemented Yet" +#endif /* GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD */ +/** @} */ diff --git a/src/ginput/ginput_keyboard.h b/src/ginput/ginput_keyboard.h new file mode 100644 index 00000000..f5737255 --- /dev/null +++ b/src/ginput/ginput_keyboard.h @@ -0,0 +1,128 @@ +/* + * 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_keyboard.h + * @brief GINPUT GFX User Input subsystem header file. + * + * @defgroup Keyboard Keyboard + * @ingroup GINPUT + * @{ + */ + +#ifndef _GINPUT_KEYBOARD_H +#define _GINPUT_KEYBOARD_H + +#if GINPUT_NEED_KEYBOARD || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +#define GINPUT_KEYBOARD_NUM_PORTS 1 // The total number of keyboard inputs + +// Event types for various ginput sources +#define GEVENT_KEYBOARD (GEVENT_GINPUT_FIRST+2) + +typedef struct GEventKeyboard_t { + GEventType type; // The type of this event (GEVENT_KEYBOARD) + uint16_t instance; // The keyboard instance + char c; // The Ascii code for the current key press. + // The only possible values are 0(NUL), 8(BS), 9(TAB), 13(CR), 27(ESC), 32(SPACE) to 126(~), 127(DEL) + // 0 indicates an extended only key. + uint16_t code; // An extended keyboard code. Codes less than 128 match their ascii equivelent. + #define GKEY_NULL 0 + #define GKEY_BACKSPACE 8 + #define GKEY_TAB 9 + #define GKEY_CR 13 + #define GKEY_ESC 27 + #define GKEY_SPACE 32 + #define GKEY_DEL 127 + #define GKEY_UP 0x0101 + #define GKEY_DOWN 0x0102 + #define GKEY_LEFT 0x0103 + #define GKEY_RIGHT 0x0104 + #define GKEY_HOME 0x0105 + #define GKEY_END 0x0106 + #define GKEY_PAGEUP 0x0107 + #define GKEY_PAGEDOWN 0x0108 + #define GKEY_INSERT 0x0109 + #define GKEY_DELETE 0x010A + #define GKEY_SHIFT 0x0201 + #define GKEY_CNTRL 0x0202 + #define GKEY_ALT 0x0203 + #define GKEY_WINKEY 0x0204 + #define GKEY_RCLKEY 0x0205 + #define GKEY_FNKEY 0x0206 + #define GKEY_FN1 0x0301 + #define GKEY_FN2 0x0302 + #define GKEY_FN3 0x0303 + #define GKEY_FN4 0x0304 + #define GKEY_FN5 0x0305 + #define GKEY_FN6 0x0306 + #define GKEY_FN7 0x0307 + #define GKEY_FN8 0x0308 + #define GKEY_FN9 0x0309 + #define GKEY_FN10 0x030A + #define GKEY_FN11 0x030B + #define GKEY_FN12 0x030C + uint16_t current_buttons; // A bit is set to indicate various meta status. + #define GMETA_KEY_DOWN 0x0001 + #define GMETA_KEY_SHIFT 0x0002 + #define GMETA_KEY_CNTRL 0x0004 + #define GMETA_KEY_ALT 0x0008 + #define GMETA_KEY_WINKEY 0x0010 + #define GMETA_KEY_RCLKKEY 0x0020 + #define GMETA_KEY_FN 0x0040 + #define GMETA_KEY_MISSED_EVENT 0x8000 + uint16_t last_buttons; // The value of current_buttons on the last event +} GEventKeyboard; + +// Keyboard Listen Flags - passed to geventAddSourceToListener() +#define GLISTEN_KEYREPEATS 0x0001 // Return key repeats (where the key is held down to get a repeat character) +#define GLISTEN_KEYCODES 0x0002 // Return all key presses including extended code key presses (not just ascii codes) +#define GLISTEN_KEYALL 0x0004 // Return keyup's, keydown's and everything in between (but not repeats unless GLISTEN_KEYREPEATS is set). +#define GLISTEN_KEYSINGLE 0x8000 // Return only when one particular extended code key is pressed or released. The particular extended code is OR'd into this value + // eg. (GLISTEN_KEYSINGLE | GKEY_CR) + // No other flags may be set with this flag. + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Create a keyboard input instance + * + * @param[in] instance The ID of the keyboard input instance (from 0 to 9999) + * + * @return The source handle of the created input instance + */ + GSourceHandle ginputGetKeyboard(uint16_t instance); + + /** + * @brief Get the current keyboard status + * + * @param[in] instance The ID of the keyboard input instance + * @param[in] pkeyboard The keyboard event struct + * + * @return Returns FALSE on an error (eg invalid instance) + */ + bool_t ginputGetKeyboardStatus(uint16_t instance, GEventKeyboard *pkeyboard); + +#ifdef __cplusplus +} +#endif + +#endif /* GINPUT_NEED_KEYBOARD */ + +#endif /* _GINPUT_KEYBOARD_H */ +/** @} */ + diff --git a/src/ginput/ginput_mouse.c b/src/ginput/ginput_mouse.c new file mode 100644 index 00000000..a0daba98 --- /dev/null +++ b/src/ginput/ginput_mouse.c @@ -0,0 +1,679 @@ +/* + * 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_mouse.c + * @brief GINPUT mouse/touch code. + * + * @defgroup Mouse Mouse + * @ingroup GINPUT + * @{ + */ +#include "gfx.h" + +#if (GFX_USE_GINPUT && GINPUT_NEED_MOUSE) || defined(__DOXYGEN__) + +#include "driver_mouse.h" + +#if GINPUT_MOUSE_NEED_CALIBRATION + #if !defined(GFX_USE_GDISP) || !GFX_USE_GDISP + #error "GINPUT: GFX_USE_GDISP must be defined when mouse or touch calibration is required" + #endif + + #include // Required for memcpy + + #define GINPUT_MOUSE_CALIBRATION_FONT "* Double" + #define GINPUT_MOUSE_CALIBRATION_FONT2 "* Narrow" + #define GINPUT_MOUSE_CALIBRATION_TEXT "Calibration" + #define GINPUT_MOUSE_CALIBRATION_ERROR_TEXT "Failed - Please try again!" + #define GINPUT_MOUSE_CALIBRATION_SAME_TEXT "Error: Same Reading - Check Driver!" + + #if GINPUT_MOUSE_MAX_CALIBRATION_ERROR < 0 + #define GINPUT_MOUSE_CALIBRATION_POINTS 3 + #else + #define GINPUT_MOUSE_CALIBRATION_POINTS 4 + #endif + + typedef struct Calibration_t { + float ax; + float bx; + float cx; + float ay; + float by; + float cy; + } Calibration; +#endif + +typedef struct MousePoint_t { + coord_t x, y; +} MousePoint; + +static GTIMER_DECL(MouseTimer); + +static struct MouseConfig_t { + MouseReading t; + MousePoint movepos; + MousePoint clickpos; + systemticks_t clicktime; + uint16_t last_buttons; + uint16_t flags; + #define FLG_INIT_DONE 0x8000 + #define FLG_CLICK_TIMER 0x0001 + #define FLG_IN_CAL 0x0010 + #define FLG_CAL_OK 0x0020 + #define FLG_CAL_SAVED 0x0040 + #define FLG_CAL_FREE 0x0080 + #define FLG_CAL_RAW 0x0100 + #if GINPUT_MOUSE_NEED_CALIBRATION + GMouseCalibrationSaveRoutine fnsavecal; + GMouseCalibrationLoadRoutine fnloadcal; + Calibration caldata; + #endif + GDisplay * display; +} MouseConfig; + +void _tsOrientClip(MouseReading *pt, GDisplay *g, bool_t doClip) { + coord_t w, h; + + w = gdispGGetWidth(g); + h = gdispGGetHeight(g); + + #if GDISP_NEED_CONTROL && !GINPUT_MOUSE_NO_ROTATION + switch(gdispGGetOrientation(g)) { + case GDISP_ROTATE_0: + break; + case GDISP_ROTATE_90: + { + coord_t t = pt->x; + pt->x = w - 1 - pt->y; + pt->y = t; + } + break; + case GDISP_ROTATE_180: + pt->x = w - 1 - pt->x; + pt->y = h - 1 - pt->y; + break; + case GDISP_ROTATE_270: + { + coord_t t = pt->y; + pt->y = h - 1 - pt->x; + pt->x = t; + } + break; + default: + break; + } + #endif + + if (doClip) { + if (pt->x < 0) pt->x = 0; + else if (pt->x >= w) pt->x = w-1; + if (pt->y < 0) pt->y = 0; + else if (pt->y >= h) pt->y = h-1; + } +} + +#if GINPUT_MOUSE_NEED_CALIBRATION + static inline void _tsSetIdentity(Calibration *c) { + c->ax = 1; + c->bx = 0; + c->cx = 0; + c->ay = 0; + c->by = 1; + c->cy = 0; + } + + static inline void _tsDrawCross(const MousePoint *pp) { + gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y, pp->x-2, pp->y, White); + gdispGDrawLine(MouseConfig.display, pp->x+2, pp->y, pp->x+15, pp->y, White); + gdispGDrawLine(MouseConfig.display, pp->x, pp->y-15, pp->x, pp->y-2, White); + gdispGDrawLine(MouseConfig.display, pp->x, pp->y+2, pp->x, pp->y+15, White); + + gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y+15, pp->x-7, pp->y+15, RGB2COLOR(184,158,131)); + gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y+7, pp->x-15, pp->y+15, RGB2COLOR(184,158,131)); + + gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y-15, pp->x-7, pp->y-15, RGB2COLOR(184,158,131)); + gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y-7, pp->x-15, pp->y-15, RGB2COLOR(184,158,131)); + + gdispGDrawLine(MouseConfig.display, pp->x+7, pp->y+15, pp->x+15, pp->y+15, RGB2COLOR(184,158,131)); + gdispGDrawLine(MouseConfig.display, pp->x+15, pp->y+7, pp->x+15, pp->y+15, RGB2COLOR(184,158,131)); + + gdispGDrawLine(MouseConfig.display, pp->x+7, pp->y-15, pp->x+15, pp->y-15, RGB2COLOR(184,158,131)); + gdispGDrawLine(MouseConfig.display, pp->x+15, pp->y-15, pp->x+15, pp->y-7, RGB2COLOR(184,158,131)); + } + + static inline void _tsClearCross(const MousePoint *pp) { + gdispGFillArea(MouseConfig.display, pp->x - 15, pp->y - 15, 42, 42, Blue); + } + + static inline void _tsTransform(MouseReading *pt, const Calibration *c) { + pt->x = (coord_t) (c->ax * pt->x + c->bx * pt->y + c->cx); + pt->y = (coord_t) (c->ay * pt->x + c->by * pt->y + c->cy); + } + + static inline void _tsDo3PointCalibration(const MousePoint *cross, const MousePoint *points, GDisplay *g, Calibration *c) { + float dx; + coord_t c0, c1, c2; + + #if GDISP_NEED_CONTROL + /* Convert all cross points back to GDISP_ROTATE_0 convention + * before calculating the calibration matrix. + */ + switch(gdispGGetOrientation(g)) { + case GDISP_ROTATE_90: + c0 = cross[0].y; + c1 = cross[1].y; + c2 = cross[2].y; + break; + case GDISP_ROTATE_180: + c0 = c1 = c2 = gdispGGetWidth(g) - 1; + c0 -= cross[0].x; + c1 -= cross[1].x; + c2 -= cross[2].x; + break; + case GDISP_ROTATE_270: + c0 = c1 = c2 = gdispGGetHeight(g) - 1; + c0 -= cross[0].y; + c1 -= cross[1].y; + c2 -= cross[2].y; + break; + case GDISP_ROTATE_0: + default: + c0 = cross[0].x; + c1 = cross[1].x; + c2 = cross[2].x; + break; + } + #else + (void) g; + + c0 = cross[0].x; + c1 = cross[1].x; + c2 = cross[2].x; + #endif + + /* Compute all the required determinants */ + dx = (float)(points[0].x - points[2].x) * (float)(points[1].y - points[2].y) + - (float)(points[1].x - points[2].x) * (float)(points[0].y - points[2].y); + + c->ax = ((float)(c0 - c2) * (float)(points[1].y - points[2].y) + - (float)(c1 - c2) * (float)(points[0].y - points[2].y)) / dx; + c->bx = ((float)(c1 - c2) * (float)(points[0].x - points[2].x) + - (float)(c0 - c2) * (float)(points[1].x - points[2].x)) / dx; + c->cx = (c0 * ((float)points[1].x * (float)points[2].y - (float)points[2].x * (float)points[1].y) + - c1 * ((float)points[0].x * (float)points[2].y - (float)points[2].x * (float)points[0].y) + + c2 * ((float)points[0].x * (float)points[1].y - (float)points[1].x * (float)points[0].y)) / dx; + + #if GDISP_NEED_CONTROL + switch(gdispGGetOrientation(g)) { + case GDISP_ROTATE_90: + c0 = c1 = c2 = gdispGGetWidth(g) - 1; + c0 -= cross[0].x; + c1 -= cross[1].x; + c2 -= cross[2].x; + break; + case GDISP_ROTATE_180: + c0 = c1 = c2 = gdispGGetHeight(g) - 1; + c0 -= cross[0].y; + c1 -= cross[1].y; + c2 -= cross[2].y; + break; + case GDISP_ROTATE_270: + c0 = cross[0].x; + c1 = cross[1].x; + c2 = cross[2].x; + break; + case GDISP_ROTATE_0: + default: + c0 = cross[0].y; + c1 = cross[1].y; + c2 = cross[2].y; + break; + } + #else + c0 = cross[0].y; + c1 = cross[1].y; + c2 = cross[2].y; + #endif + + c->ay = ((float)(c0 - c2) * (float)(points[1].y - points[2].y) + - (float)(c1 - c2) * (float)(points[0].y - points[2].y)) / dx; + c->by = ((float)(c1 - c2) * (float)(points[0].x - points[2].x) + - (float)(c0 - c2) * (float)(points[1].x - points[2].x)) / dx; + c->cy = (c0 * ((float)points[1].x * (float)points[2].y - (float)points[2].x * (float)points[1].y) + - c1 * ((float)points[0].x * (float)points[2].y - (float)points[2].x * (float)points[0].y) + + c2 * ((float)points[0].x * (float)points[1].y - (float)points[1].x * (float)points[0].y)) / dx; + } +#endif + +#if GINPUT_MOUSE_READ_CYCLES > 1 + static void get_raw_reading(MouseReading *pt) { + int32_t x, y, z; + unsigned i; + + x = y = z = 0; + for(i = 0; i < GINPUT_MOUSE_READ_CYCLES; i++) { + ginput_lld_mouse_get_reading(pt); + x += pt->x; + y += pt->y; + z += pt->z; + } + + /* Take the average of the readings */ + pt->x = x / GINPUT_MOUSE_READ_CYCLES; + pt->y = y / GINPUT_MOUSE_READ_CYCLES; + pt->z = z / GINPUT_MOUSE_READ_CYCLES; + } +#else + #define get_raw_reading(pt) ginput_lld_mouse_get_reading(pt) +#endif + +static void get_calibrated_reading(MouseReading *pt) { + get_raw_reading(pt); + + #if GINPUT_MOUSE_NEED_CALIBRATION + _tsTransform(pt, &MouseConfig.caldata); + #endif + + _tsOrientClip(pt, MouseConfig.display, !(MouseConfig.flags & FLG_CAL_RAW)); +} + +static void MousePoll(void *param) { + (void) param; + GSourceListener *psl; + GEventMouse *pe; + unsigned meta; + uint16_t upbtns, dnbtns; + uint32_t cdiff; + uint32_t mdiff; + + // Save the last mouse state + MouseConfig.last_buttons = MouseConfig.t.buttons; + + // Get the new mouse reading + get_calibrated_reading(&MouseConfig.t); + + // Calculate out new event meta value and handle CLICK and CXTCLICK + dnbtns = MouseConfig.t.buttons & ~MouseConfig.last_buttons; + upbtns = ~MouseConfig.t.buttons & MouseConfig.last_buttons; + meta = GMETA_NONE; + + // As the touch moves up we need to return a point at the old position because some + // controllers return garbage with the mouse up + if ((upbtns & GINPUT_MOUSE_BTN_LEFT)) { + MouseConfig.t.x = MouseConfig.movepos.x; + MouseConfig.t.y = MouseConfig.movepos.y; + } + + // Calculate the position difference from our movement reference (update the reference if out of range) + mdiff = (MouseConfig.t.x - MouseConfig.movepos.x) * (MouseConfig.t.x - MouseConfig.movepos.x) + + (MouseConfig.t.y - MouseConfig.movepos.y) * (MouseConfig.t.y - MouseConfig.movepos.y); + if (mdiff > GINPUT_MOUSE_MAX_MOVE_JITTER * GINPUT_MOUSE_MAX_MOVE_JITTER) { + MouseConfig.movepos.x = MouseConfig.t.x; + MouseConfig.movepos.y = MouseConfig.t.y; + } + + // Check if the click has moved outside the click area and if so cancel the click + if ((MouseConfig.flags & FLG_CLICK_TIMER)) { + cdiff = (MouseConfig.t.x - MouseConfig.clickpos.x) * (MouseConfig.t.x - MouseConfig.clickpos.x) + + (MouseConfig.t.y - MouseConfig.clickpos.y) * (MouseConfig.t.y - MouseConfig.clickpos.y); + if (cdiff > GINPUT_MOUSE_MAX_CLICK_JITTER * GINPUT_MOUSE_MAX_CLICK_JITTER) + MouseConfig.flags &= ~FLG_CLICK_TIMER; + } + + // Mouse down + if ((dnbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) { + MouseConfig.clickpos.x = MouseConfig.t.x; + MouseConfig.clickpos.y = MouseConfig.t.y; + MouseConfig.clicktime = gfxSystemTicks(); + MouseConfig.flags |= FLG_CLICK_TIMER; + if ((dnbtns & GINPUT_MOUSE_BTN_LEFT)) + meta |= GMETA_MOUSE_DOWN; + } + + // Mouse up + if ((upbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) { + if ((upbtns & GINPUT_MOUSE_BTN_LEFT)) + meta |= GMETA_MOUSE_UP; + if ((MouseConfig.flags & FLG_CLICK_TIMER)) { + if ((upbtns & GINPUT_MOUSE_BTN_LEFT) + #if GINPUT_MOUSE_CLICK_TIME != TIME_INFINITE + && gfxSystemTicks() - MouseConfig.clicktime < gfxMillisecondsToTicks(GINPUT_MOUSE_CLICK_TIME) + #endif + ) + meta |= GMETA_MOUSE_CLICK; + else + meta |= GMETA_MOUSE_CXTCLICK; + MouseConfig.flags &= ~FLG_CLICK_TIMER; + } + } + + // Send the event to the listeners that are interested. + psl = 0; + while ((psl = geventGetSourceListener((GSourceHandle)(&MouseConfig), psl))) { + if (!(pe = (GEventMouse *)geventGetEventBuffer(psl))) { + // This listener is missing - save the meta events that have happened + psl->srcflags |= meta; + continue; + } + + // If we haven't really moved (and there are no meta events) don't bother sending the event + if (mdiff <= GINPUT_MOUSE_MAX_MOVE_JITTER * GINPUT_MOUSE_MAX_MOVE_JITTER && !psl->srcflags + && !meta && MouseConfig.last_buttons == MouseConfig.t.buttons && !(psl->listenflags & GLISTEN_MOUSENOFILTER)) + continue; + + // Send the event if we are listening for it + if (((MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEDOWNMOVES)) + || (!(MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEUPMOVES)) + || (meta && (psl->listenflags & GLISTEN_MOUSEMETA))) { + pe->type = GINPUT_MOUSE_EVENT_TYPE; + pe->instance = 0; + pe->x = MouseConfig.t.x; + pe->y = MouseConfig.t.y; + pe->z = MouseConfig.t.z; + pe->current_buttons = MouseConfig.t.buttons; + pe->last_buttons = MouseConfig.last_buttons; + pe->meta = meta; + if (psl->srcflags) { + pe->current_buttons |= GINPUT_MISSED_MOUSE_EVENT; + pe->meta |= psl->srcflags; + psl->srcflags = 0; + } + pe->display = MouseConfig.display; + geventSendEvent(psl); + } + } +} + +GSourceHandle ginputGetMouse(uint16_t instance) { + #if GINPUT_MOUSE_NEED_CALIBRATION + Calibration *pc; + #endif + + // We only support a single mouse instance currently + // Instance 9999 is the same as instance 0 except that it installs + // a special "raw" calibration if there isn't one we can load. + if (instance && instance != 9999) + return 0; + + // Make sure we have a valid mouse display + if (!MouseConfig.display) + MouseConfig.display = GDISP; + + // Do we need to initialise the mouse subsystem? + if (!(MouseConfig.flags & FLG_INIT_DONE)) { + ginput_lld_mouse_init(); + + #if GINPUT_MOUSE_NEED_CALIBRATION + #if GINPUT_MOUSE_LLD_CALIBRATION_LOADSAVE + if (!MouseConfig.fnloadcal) { + MouseConfig.fnloadcal = ginput_lld_mouse_calibration_load; + MouseConfig.flags &= ~FLG_CAL_FREE; + } + if (!MouseConfig.fnsavecal) + MouseConfig.fnsavecal = ginput_lld_mouse_calibration_save; + #endif + if (MouseConfig.fnloadcal && (pc = (Calibration *)MouseConfig.fnloadcal(instance))) { + memcpy(&MouseConfig.caldata, pc, sizeof(MouseConfig.caldata)); + MouseConfig.flags |= (FLG_CAL_OK|FLG_CAL_SAVED); + if ((MouseConfig.flags & FLG_CAL_FREE)) + gfxFree((void *)pc); + } else if (instance == 9999) { + _tsSetIdentity(&MouseConfig.caldata); + MouseConfig.flags |= (FLG_CAL_OK|FLG_CAL_SAVED|FLG_CAL_RAW); + } else + ginputCalibrateMouse(instance); + #endif + + // Get the first reading + MouseConfig.last_buttons = 0; + get_calibrated_reading(&MouseConfig.t); + + // Mark init as done and start the Poll timer + MouseConfig.flags |= FLG_INIT_DONE; + gtimerStart(&MouseTimer, MousePoll, 0, TRUE, GINPUT_MOUSE_POLL_PERIOD); + } + + // Return our structure as the handle + return (GSourceHandle)&MouseConfig; +} + +void ginputSetMouseDisplay(uint16_t instance, GDisplay *g) { + if (instance) + return; + + MouseConfig.display = g ? g : GDISP; +} + +GDisplay *ginputGetMouseDisplay(uint16_t instance) { + if (instance) + return 0; + + return MouseConfig.display; +} + +bool_t ginputGetMouseStatus(uint16_t instance, GEventMouse *pe) { + // Win32 threads don't seem to recognise priority and/or pre-emption + // so we add a sleep here to prevent 100% polled applications from locking up. + gfxSleepMilliseconds(1); + + if (instance || (MouseConfig.flags & (FLG_INIT_DONE|FLG_IN_CAL)) != FLG_INIT_DONE) + return FALSE; + + pe->type = GINPUT_MOUSE_EVENT_TYPE; + pe->instance = instance; + pe->x = MouseConfig.t.x; + pe->y = MouseConfig.t.y; + pe->z = MouseConfig.t.z; + pe->current_buttons = MouseConfig.t.buttons; + pe->last_buttons = MouseConfig.last_buttons; + if (pe->current_buttons & ~pe->last_buttons & GINPUT_MOUSE_BTN_LEFT) + pe->meta = GMETA_MOUSE_DOWN; + else if (~pe->current_buttons & pe->last_buttons & GINPUT_MOUSE_BTN_LEFT) + pe->meta = GMETA_MOUSE_UP; + else + pe->meta = GMETA_NONE; + return TRUE; +} + +bool_t ginputCalibrateMouse(uint16_t instance) { + #if !GINPUT_MOUSE_NEED_CALIBRATION + (void) instance; + + return FALSE; + #else + + const coord_t height = gdispGGetHeight(MouseConfig.display); + const coord_t width = gdispGGetWidth(MouseConfig.display); + #if GINPUT_MOUSE_CALIBRATE_EXTREMES + const MousePoint cross[] = {{0, 0}, + {(width - 1) , 0}, + {(width - 1) , (height - 1)}, + {(width / 2), (height / 2)}}; /* Check point */ + #else + const MousePoint cross[] = {{(width / 4), (height / 4)}, + {(width - (width / 4)) , (height / 4)}, + {(width - (width / 4)) , (height - (height / 4))}, + {(width / 2), (height / 2)}}; /* Check point */ + #endif + MousePoint points[GINPUT_MOUSE_CALIBRATION_POINTS]; + const MousePoint *pc; + MousePoint *pt; + int32_t px, py; + unsigned i, j; + font_t font1, font2; + #if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0 + unsigned err; + #endif + + if (instance || (MouseConfig.flags & FLG_IN_CAL)) + return FALSE; + + font1 = gdispOpenFont(GINPUT_MOUSE_CALIBRATION_FONT); + font2 = gdispOpenFont(GINPUT_MOUSE_CALIBRATION_FONT2); + + MouseConfig.flags |= FLG_IN_CAL; + gtimerStop(&MouseTimer); + MouseConfig.flags &= ~(FLG_CAL_OK|FLG_CAL_SAVED|FLG_CAL_RAW); + + #if GDISP_NEED_CLIP + gdispGSetClip(MouseConfig.display, 0, 0, width, height); + #endif + + #if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0 + while(1) { + #endif + gdispGClear(MouseConfig.display, Blue); + + gdispGFillStringBox(MouseConfig.display, 0, 5, width, 30, GINPUT_MOUSE_CALIBRATION_TEXT, font1, White, Blue, justifyCenter); + + for(i = 0, pt = points, pc = cross; i < GINPUT_MOUSE_CALIBRATION_POINTS; i++, pt++, pc++) { + _tsDrawCross(pc); + + do { + + /* Wait for the mouse to be pressed */ + while(get_raw_reading(&MouseConfig.t), !(MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT)) + gfxSleepMilliseconds(20); + + /* Average all the samples while the mouse is down */ + for(px = py = 0, j = 0; + gfxSleepMilliseconds(20), /* Settling time between readings */ + get_raw_reading(&MouseConfig.t), + (MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT); + j++) { + px += MouseConfig.t.x; + py += MouseConfig.t.y; + } + + } while(!j); + + pt->x = px / j; + pt->y = py / j; + + _tsClearCross(pc); + + if (i >= 1 && pt->x == (pt-1)->x && pt->y == (pt-1)->y) { + gdispGFillStringBox(MouseConfig.display, 0, 35, width, 40, GINPUT_MOUSE_CALIBRATION_SAME_TEXT, font2, Red, Yellow, justifyCenter); + gfxSleepMilliseconds(5000); + gdispGFillArea(MouseConfig.display, 0, 35, width, 40, Blue); + } + + } + + /* Apply 3 point calibration algorithm */ + _tsDo3PointCalibration(cross, points, MouseConfig.display, &MouseConfig.caldata); + + /* Verification of correctness of calibration (optional) : + * See if the 4th point (Middle of the screen) coincides with the calibrated + * result. If point is within +/- Squareroot(ERROR) pixel margin, then successful calibration + * Else, start from the beginning. + */ + #if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0 + /* Transform the co-ordinates */ + MouseConfig.t.x = points[3].x; + MouseConfig.t.y = points[3].y; + _tsTransform(&MouseConfig.t, &MouseConfig.caldata); + _tsOrientClip(&MouseConfig.t, MouseConfig.display, FALSE); + + /* Calculate the delta */ + err = (MouseConfig.t.x - cross[3].x) * (MouseConfig.t.x - cross[3].x) + + (MouseConfig.t.y - cross[3].y) * (MouseConfig.t.y - cross[3].y); + + if (err <= GINPUT_MOUSE_MAX_CALIBRATION_ERROR * GINPUT_MOUSE_MAX_CALIBRATION_ERROR) + break; + + gdispGFillStringBox(MouseConfig.display, 0, 35, width, 40, GINPUT_MOUSE_CALIBRATION_ERROR_TEXT, font2, Red, Yellow, justifyCenter); + gfxSleepMilliseconds(5000); + } + #endif + + // Restart everything + gdispCloseFont(font1); + gdispCloseFont(font2); + MouseConfig.flags |= FLG_CAL_OK; + MouseConfig.last_buttons = 0; + get_calibrated_reading(&MouseConfig.t); + MouseConfig.flags &= ~FLG_IN_CAL; + if ((MouseConfig.flags & FLG_INIT_DONE)) + gtimerStart(&MouseTimer, MousePoll, 0, TRUE, GINPUT_MOUSE_POLL_PERIOD); + + // Save the calibration data (if possible) + if (MouseConfig.fnsavecal) { + MouseConfig.fnsavecal(instance, (const uint8_t *)&MouseConfig.caldata, sizeof(MouseConfig.caldata)); + MouseConfig.flags |= FLG_CAL_SAVED; + } + + // Clear the screen using the GWIN default background color + #if GFX_USE_GWIN + gdispGClear(MouseConfig.display, gwinGetDefaultBgColor()); + #else + gdispGClear(MouseConfig.display, Black); + #endif + + return TRUE; + #endif +} + +/* Set the routines to save and fetch calibration data. + * This function should be called before first calling ginputGetMouse() for a particular instance + * as the ginputGetMouse() routine may attempt to fetch calibration data and perform a startup calibration if there is no way to get it. + * If this is called after ginputGetMouse() has been called and the driver requires calibration storage, it will immediately save the data is has already obtained. + * The 'requireFree' parameter indicates if the fetch buffer must be free()'d to deallocate the buffer provided by the Fetch routine. + */ +void ginputSetMouseCalibrationRoutines(uint16_t instance, GMouseCalibrationSaveRoutine fnsave, GMouseCalibrationLoadRoutine fnload, bool_t requireFree) { + #if GINPUT_MOUSE_NEED_CALIBRATION + if (instance) + return; + + MouseConfig.fnloadcal = fnload; + MouseConfig.fnsavecal = fnsave; + if (requireFree) + MouseConfig.flags |= FLG_CAL_FREE; + else + MouseConfig.flags &= ~FLG_CAL_FREE; + #if GINPUT_MOUSE_LLD_CALIBRATION_LOADSAVE + if (!MouseConfig.fnloadcal) { + MouseConfig.fnloadcal = ginput_lld_mouse_calibration_load; + MouseConfig.flags &= ~FLG_CAL_FREE; + } + if (!MouseConfig.fnsavecal) + MouseConfig.fnsavecal = ginput_lld_mouse_calibration_save; + #endif + if (MouseConfig.fnsavecal && (MouseConfig.flags & (FLG_CAL_OK|FLG_CAL_SAVED)) == FLG_CAL_OK) { + MouseConfig.fnsavecal(instance, (const uint8_t *)&MouseConfig.caldata, sizeof(MouseConfig.caldata)); + MouseConfig.flags |= FLG_CAL_SAVED; + } + #else + (void)instance, (void)fnsave, (void)fnload, (void)requireFree; + #endif +} + +/* Test if a particular mouse instance requires routines to save its calibration data. */ +bool_t ginputRequireMouseCalibrationStorage(uint16_t instance) { + if (instance) + return FALSE; + + #if GINPUT_MOUSE_NEED_CALIBRATION && !GINPUT_MOUSE_LLD_CALIBRATION_LOADSAVE + return TRUE; + #else + return FALSE; + #endif +} + +/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */ +void ginputMouseWakeup(void) { + gtimerJab(&MouseTimer); +} + +/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */ +void ginputMouseWakeupI(void) { + gtimerJabI(&MouseTimer); +} + +#endif /* GFX_USE_GINPUT && GINPUT_NEED_MOUSE */ +/** @} */ diff --git a/src/ginput/ginput_mouse.h b/src/ginput/ginput_mouse.h new file mode 100644 index 00000000..61ad35e0 --- /dev/null +++ b/src/ginput/ginput_mouse.h @@ -0,0 +1,181 @@ +/* + * 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_mouse.h + * @brief GINPUT GFX User Input subsystem header file for mouse and touch. + * + * @defgroup Mouse Mouse + * @ingroup GINPUT + * + * @details GINPUT allows it to easily interface touchscreens and mices to + * your application. + * + * @pre GFX_USE_GINPUT must be set to TRUE in your gfxconf.h + * @pre GINPUT_NEED_MOUSE must be set to TRUE in your gfxconf.h + * + * @{ + */ + +#ifndef _GINPUT_MOUSE_H +#define _GINPUT_MOUSE_H + +#if GINPUT_NEED_MOUSE || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +/* This type definition is also used by touch */ +typedef struct GEventMouse_t { + GEventType type; // The type of this event (GEVENT_MOUSE or GEVENT_TOUCH) + uint16_t instance; // The mouse/touch instance + coord_t x, y, z; // The position of the mouse. + // - For touch devices, Z is the current pressure if supported (otherwise 0) + // - For mice, Z is the 3rd dimension if supported (otherwise 0) + uint16_t current_buttons; // A bit is set if the button is down. + // - For touch only bit 0 is relevant + // - For mice the order of the buttons is (from 0 to n) left, right, middle, any other buttons + // - Bit 15 being set indicates that an important mouse event has been missed. + #define GINPUT_MOUSE_BTN_LEFT 0x0001 + #define GINPUT_MOUSE_BTN_RIGHT 0x0002 + #define GINPUT_MOUSE_BTN_MIDDLE 0x0004 + #define GINPUT_MOUSE_BTN_4 0x0008 + #define GINPUT_MISSED_MOUSE_EVENT 0x8000 + #define GINPUT_TOUCH_PRESSED GINPUT_MOUSE_BTN_LEFT + uint16_t last_buttons; // The value of current_buttons on the last event + enum GMouseMeta_e { + GMETA_NONE = 0, // There is no meta event currently happening + GMETA_MOUSE_DOWN = 1, // Button 0 has just gone down + GMETA_MOUSE_UP = 2, // Button 0 has just gone up + GMETA_MOUSE_CLICK = 4, // Button 0 has just gone through a short down - up cycle + GMETA_MOUSE_CXTCLICK = 8 // For mice - The right button has just been depressed + // For touch - a long press has just occurred + } meta; + GDisplay * display; // The display this mouse is currently associated with. + } GEventMouse; + +// Mouse/Touch Listen Flags - passed to geventAddSourceToListener() +#define GLISTEN_MOUSEMETA 0x0001 // Create events for meta events such as CLICK and CXTCLICK +#define GLISTEN_MOUSEDOWNMOVES 0x0002 // Creates mouse move events when the primary mouse button is down (touch is on the surface) +#define GLISTEN_MOUSEUPMOVES 0x0004 // Creates mouse move events when the primary mouse button is up (touch is off the surface - if the hardware allows). +#define GLISTEN_MOUSENOFILTER 0x0008 // Don't filter out mouse moves where the position hasn't changed. +#define GLISTEN_TOUCHMETA GLISTEN_MOUSEMETA +#define GLISTEN_TOUCHDOWNMOVES GLISTEN_MOUSEDOWNMOVES +#define GLISTEN_TOUCHUPMOVES GLISTEN_MOUSEUPMOVES +#define GLISTEN_TOUCHNOFILTER GLISTEN_MOUSENOFILTER + +#define GINPUT_MOUSE_NUM_PORTS 1 // The total number of mouse/touch inputs supported + +// Event types for the mouse ginput source +#define GEVENT_MOUSE (GEVENT_GINPUT_FIRST+0) +#define GEVENT_TOUCH (GEVENT_GINPUT_FIRST+1) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Creates an instance of a mouse and returns the Source handler + * @note HACK: if the instance is 9999, it is treated as instance 0 except + * that no calibration will be performed! + * + * @param[in] instance The ID of the mouse input instance (from 0 to 9999) + * + * @return The source handle of the created instance + */ + GSourceHandle ginputGetMouse(uint16_t instance); + + /** + * @brief Assign the display associated with the mouse + * @note This only needs to be called if the mouse is associated with a display + * other than the current default display. It must be called before + * @p ginputGetMouse() if the new display is to be used during the calibration + * process. Other than calibration the display is used for range checking, + * and may also be used to display a mouse pointer. + * + * @param[in] instance The ID of the mouse input instance + * @param[in] g The GDisplay to which this mouse belongs + */ + void ginputSetMouseDisplay(uint16_t instance, GDisplay *g); + + /** + * @brief Get the display currently associated with the mouse + * @return A pointer to the display + * + * @param[in] instance The ID of the mouse input instance + */ + GDisplay *ginputGetMouseDisplay(uint16_t instance); + + /** + * @brief Get the current mouse position and button status + * @note Unlinke a listener event, this status cannot record meta events such as + * "CLICK". + * + * @param[in] instance The ID of the mouse input instance + * @param[in] pmouse The mouse event + * + * @return FALSE on an error (eg. invalid instance) + */ + bool_t ginputGetMouseStatus(uint16_t instance, GEventMouse *pmouse); + + /** + * @brief Performs a calibration + * + * @param[in] instance The ID of the mouse input instance + * + * @return FALSE if the driver dosen't support a calibration of if the handle is invalid + */ + bool_t ginputCalibrateMouse(uint16_t instance); + + /* Set the routines to save and fetch calibration data. + * This function should be called before first calling ginputGetMouse() for a particular instance + * as the gdispGetMouse() routine may attempt to fetch calibration data and perform a startup calibration if there is no way to get it. + * If this is called after gdispGetMouse() has been called and the driver requires calibration storage, it will immediately save the data is has already obtained. + * The 'requireFree' parameter indicates if the fetch buffer must be free()'d to deallocate the buffer provided by the Fetch routine. + */ + typedef void (*GMouseCalibrationSaveRoutine)(uint16_t instance, const uint8_t *calbuf, size_t sz); // Save calibration data + typedef const char * (*GMouseCalibrationLoadRoutine)(uint16_t instance); // Load calibration data (returns NULL if not data saved) + + /** + * @brief Set the routines to store and restore calibration data + * + * @details This function should be called before first calling ginputGetMouse() for a particular instance + * as the gdispGetMouse() routine may attempt to fetch calibration data and perform a startup calibration if there is no way to get it. + * If this is called after gdispGetMouse() has been called and the driver requires calibration storage, it will immediately save the + * data is has already obtained. + * + * @param[in] instance The ID of the mouse input instance + * @param[in] fnsave The routine to save the data + * @param[in] fnload The routine to restore the data + * @param[in] requireFree TRUE if the buffer returned by the load function must be freed by the mouse code. + */ + void ginputSetMouseCalibrationRoutines(uint16_t instance, GMouseCalibrationSaveRoutine fnsave, GMouseCalibrationLoadRoutine fnload, bool_t requireFree); + + /** + * @brief Test if a particular mouse/touch instance requires routines to save it's alibration data + * @note Not implemented yet + * + * @param[in] instance The ID of the mouse input instance + * + * @return TRUE if needed + */ + bool_t ginputRequireMouseCalibrationStorage(uint16_t instance); + +#ifdef __cplusplus +} +#endif + +#endif /* GINPUT_NEED_MOUSE */ + +#endif /* _GINPUT_MOUSE_H */ +/** @} */ + diff --git a/src/ginput/ginput_toggle.c b/src/ginput/ginput_toggle.c new file mode 100644 index 00000000..4c6dc9ae --- /dev/null +++ b/src/ginput/ginput_toggle.c @@ -0,0 +1,156 @@ +/* + * 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_toggle.c + * @brief GINPUT toggle code. + * + * @defgroup Toggle Toggle + * @ingroup GINPUT + * @{ + */ +#include "gfx.h" + +#if (GFX_USE_GINPUT && GINPUT_NEED_TOGGLE) || defined(__DOXYGEN__) + +#include "driver_toggle.h" + +#define GINPUT_TOGGLE_ISON 0x01 +#define GINPUT_TOGGLE_INVERT 0x02 + +static GTIMER_DECL(ToggleTimer); +static struct GEventToggleStatus_t { + uint8_t status; +} ToggleStatus[GINPUT_TOGGLE_NUM_PORTS]; + +// Our polling function +static void TogglePoll(void *param) { + (void) param; + + const GToggleConfig *ptc; + GSourceListener *psl; + GEventToggle *pe; + unsigned i, bits, mask; + uint8_t state; + + // Loop while there are bits to get + for(ptc = GInputToggleConfigTable, i=0; i < GINPUT_TOGGLE_NUM_PORTS; ptc++) { + + // Get the next block of bits + bits = ginput_lld_toggle_getbits(ptc) ^ ptc->invert; + + // Extract the bits of use + for(mask = ptc->mask; i < GINPUT_TOGGLE_NUM_PORTS && mask; mask >>= 1, bits >>= 1) { + // Ignore bits not in our mask + if (!(mask & 1)) + continue; + + // Calculate our new state + state = ToggleStatus[i].status & ~GINPUT_TOGGLE_ISON; + if (state & GINPUT_TOGGLE_INVERT) + bits ^= 1; + if (bits & 1) + state |= GINPUT_TOGGLE_ISON; + + // Has it changed? + if ((state ^ ToggleStatus[i].status) & GINPUT_TOGGLE_ISON) { + + // Save the new state + ToggleStatus[i].status = state; + + // Send the event to the listeners that are interested. + psl = 0; + while ((psl = geventGetSourceListener((GSourceHandle)(ToggleStatus+i), psl))) { + if (!(pe = (GEventToggle *)geventGetEventBuffer(psl))) + continue; + if ((state & GINPUT_TOGGLE_ISON)) { + if ((psl->listenflags & GLISTEN_TOGGLE_ON)) { + pe->type = GEVENT_TOGGLE; + pe->instance = i; + pe->on = TRUE; + geventSendEvent(psl); + } + } else { + if ((psl->listenflags & GLISTEN_TOGGLE_OFF)) { + pe->type = GEVENT_TOGGLE; + pe->instance = i; + pe->on = FALSE; + geventSendEvent(psl); + } + } + } + } + + // Next toggle switch + i++; + } + } +} + +/* Hardware Toggle/Switch/Button Functions */ +GSourceHandle ginputGetToggle(uint16_t instance) { + const GToggleConfig *ptc; + + if (instance >= GINPUT_TOGGLE_NUM_PORTS) + return 0; + + // Do we need to initialise the toggle subsystem? + if (!gtimerIsActive(&ToggleTimer)) { + for(ptc = GInputToggleConfigTable; ptc < GInputToggleConfigTable+sizeof(GInputToggleConfigTable)/sizeof(GInputToggleConfigTable[0]); ptc++) + ginput_lld_toggle_init(ptc); + gtimerStart(&ToggleTimer, TogglePoll, 0, TRUE, GINPUT_TOGGLE_POLL_PERIOD); + } + + // OK - return this input + return (GSourceHandle)(ToggleStatus+instance); +} + +// If invert is true, invert the on/off sense for the toggle +void ginputInvertToggle(uint16_t instance, bool_t invert) { + if (instance >= GINPUT_TOGGLE_NUM_PORTS) + return; + if (invert) { + if (!(ToggleStatus[instance].status & GINPUT_TOGGLE_INVERT)) { + ToggleStatus[instance].status |= GINPUT_TOGGLE_INVERT; + ToggleStatus[instance].status ^= GINPUT_TOGGLE_ISON; + } + } else { + if ((ToggleStatus[instance].status & GINPUT_TOGGLE_INVERT)) { + ToggleStatus[instance].status &= ~GINPUT_TOGGLE_INVERT; + ToggleStatus[instance].status ^= GINPUT_TOGGLE_ISON; + } + } +} + +/* Get the current toggle status. + * Returns FALSE on error (eg invalid instance) + */ +bool_t ginputGetToggleStatus(uint16_t instance, GEventToggle *ptoggle) { + // Win32 threads don't seem to recognise priority and/or pre-emption + // so we add a sleep here to prevent 100% polled applications from locking up. + gfxSleepMilliseconds(1); + + if (instance >= GINPUT_TOGGLE_NUM_PORTS) + return FALSE; + ptoggle->type = GEVENT_TOGGLE; + ptoggle->instance = instance; + ptoggle->on = (ToggleStatus[instance].status & GINPUT_TOGGLE_ISON) ? TRUE : FALSE; + return TRUE; +} + +/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */ +void ginputToggleWakeup(void) { + gtimerJab(&ToggleTimer); +} + +/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */ +void ginputToggleWakeupI(void) { + gtimerJabI(&ToggleTimer); +} + +#endif /* GFX_USE_GINPUT && GINPUT_NEED_TOGGLE */ +/** @} */ diff --git a/src/ginput/ginput_toggle.h b/src/ginput/ginput_toggle.h new file mode 100644 index 00000000..7fc075d6 --- /dev/null +++ b/src/ginput/ginput_toggle.h @@ -0,0 +1,100 @@ +/* + * 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_toggle.h + * @brief GINPUT GFX User Input subsystem header file. + * + * @defgroup Toggle Toggle + * @ingroup GINPUT + * + * @details GINPUT allows it to interface toggle buttons easily to your + * application. + * + * @pre GFX_USE_GINPUT must be set to TRUE in your gfxconf.h + * @pre GINPUT_NEED_TOGGLE must be set to TRUE in your gfxconf.h + * + * @{ + */ + +#ifndef _GINPUT_TOGGLE_H +#define _GINPUT_TOGGLE_H + +#if GINPUT_NEED_TOGGLE || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Low Level Driver details and error checks. */ +/*===========================================================================*/ + +// Get the hardware definitions - Number of instances etc. +#include "ginput_lld_toggle_config.h" + +#ifndef GINPUT_TOGGLE_POLL_PERIOD + #define GINPUT_TOGGLE_POLL_PERIOD 200 +#endif + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +// Event types for various ginput sources +#define GEVENT_TOGGLE (GEVENT_GINPUT_FIRST+3) + +typedef struct GEventToggle_t { + GEventType type; // The type of this event (GEVENT_TOGGLE) + uint16_t instance; // The toggle instance + bool_t on; // True if the toggle/button is on + } GEventToggle; + +// Toggle Listen Flags - passed to geventAddSourceToListener() +#define GLISTEN_TOGGLE_ON 0x0001 // Return an event when the toggle turns on +#define GLISTEN_TOGGLE_OFF 0x0002 // Return an event when the toggle turns off + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Create a toggle input instance + * + * @param[in] instance The ID of the toggle input instance (from 0 to 9999) + * + * @return The source handle of the created instance + */ + GSourceHandle ginputGetToggle(uint16_t instance); + + /** + * @brief Can be used to invert the sense of a toggle + * + * @param[in] instance The ID of the toggle input instance + * @param[in] invert If TRUE, will be inverted + */ + void ginputInvertToggle(uint16_t instance, bool_t invert); + + /** + * @brief Get the current toggle status + * + * @param[in] instance The ID of the toggle input instance + * @param[in] ptoggle The toggle event struct + * + * @return Returns FALSE on an error (eg invalid instance) + */ + bool_t ginputGetToggleStatus(uint16_t instance, GEventToggle *ptoggle); + +#ifdef __cplusplus +} +#endif + +#endif /* GINPUT_NEED_TOGGLE */ + +#endif /* _GINPUT_TOGGLE_H */ +/** @} */ + diff --git a/src/ginput/keyboard.c b/src/ginput/keyboard.c deleted file mode 100644 index 98fad414..00000000 --- a/src/ginput/keyboard.c +++ /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/ginput/keyboard.c - * @brief GINPUT keyboard code. - * - * @defgroup Keyboard Keyboard - * @ingroup GINPUT - * - * @{ - */ - -#include "gfx.h" - -#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || defined(__DOXYGEN__) - #error "GINPUT: GINPUT_NEED_KEYBOARD - Not Implemented Yet" -#endif /* GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD */ -/** @} */ diff --git a/src/ginput/keyboard.h b/src/ginput/keyboard.h deleted file mode 100644 index eff5cc6f..00000000 --- a/src/ginput/keyboard.h +++ /dev/null @@ -1,128 +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.h - * @brief GINPUT GFX User Input subsystem header file. - * - * @defgroup Keyboard Keyboard - * @ingroup GINPUT - * @{ - */ - -#ifndef _GINPUT_KEYBOARD_H -#define _GINPUT_KEYBOARD_H - -#if GINPUT_NEED_KEYBOARD || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -#define GINPUT_KEYBOARD_NUM_PORTS 1 // The total number of keyboard inputs - -// Event types for various ginput sources -#define GEVENT_KEYBOARD (GEVENT_GINPUT_FIRST+2) - -typedef struct GEventKeyboard_t { - GEventType type; // The type of this event (GEVENT_KEYBOARD) - uint16_t instance; // The keyboard instance - char c; // The Ascii code for the current key press. - // The only possible values are 0(NUL), 8(BS), 9(TAB), 13(CR), 27(ESC), 32(SPACE) to 126(~), 127(DEL) - // 0 indicates an extended only key. - uint16_t code; // An extended keyboard code. Codes less than 128 match their ascii equivelent. - #define GKEY_NULL 0 - #define GKEY_BACKSPACE 8 - #define GKEY_TAB 9 - #define GKEY_CR 13 - #define GKEY_ESC 27 - #define GKEY_SPACE 32 - #define GKEY_DEL 127 - #define GKEY_UP 0x0101 - #define GKEY_DOWN 0x0102 - #define GKEY_LEFT 0x0103 - #define GKEY_RIGHT 0x0104 - #define GKEY_HOME 0x0105 - #define GKEY_END 0x0106 - #define GKEY_PAGEUP 0x0107 - #define GKEY_PAGEDOWN 0x0108 - #define GKEY_INSERT 0x0109 - #define GKEY_DELETE 0x010A - #define GKEY_SHIFT 0x0201 - #define GKEY_CNTRL 0x0202 - #define GKEY_ALT 0x0203 - #define GKEY_WINKEY 0x0204 - #define GKEY_RCLKEY 0x0205 - #define GKEY_FNKEY 0x0206 - #define GKEY_FN1 0x0301 - #define GKEY_FN2 0x0302 - #define GKEY_FN3 0x0303 - #define GKEY_FN4 0x0304 - #define GKEY_FN5 0x0305 - #define GKEY_FN6 0x0306 - #define GKEY_FN7 0x0307 - #define GKEY_FN8 0x0308 - #define GKEY_FN9 0x0309 - #define GKEY_FN10 0x030A - #define GKEY_FN11 0x030B - #define GKEY_FN12 0x030C - uint16_t current_buttons; // A bit is set to indicate various meta status. - #define GMETA_KEY_DOWN 0x0001 - #define GMETA_KEY_SHIFT 0x0002 - #define GMETA_KEY_CNTRL 0x0004 - #define GMETA_KEY_ALT 0x0008 - #define GMETA_KEY_WINKEY 0x0010 - #define GMETA_KEY_RCLKKEY 0x0020 - #define GMETA_KEY_FN 0x0040 - #define GMETA_KEY_MISSED_EVENT 0x8000 - uint16_t last_buttons; // The value of current_buttons on the last event -} GEventKeyboard; - -// Keyboard Listen Flags - passed to geventAddSourceToListener() -#define GLISTEN_KEYREPEATS 0x0001 // Return key repeats (where the key is held down to get a repeat character) -#define GLISTEN_KEYCODES 0x0002 // Return all key presses including extended code key presses (not just ascii codes) -#define GLISTEN_KEYALL 0x0004 // Return keyup's, keydown's and everything in between (but not repeats unless GLISTEN_KEYREPEATS is set). -#define GLISTEN_KEYSINGLE 0x8000 // Return only when one particular extended code key is pressed or released. The particular extended code is OR'd into this value - // eg. (GLISTEN_KEYSINGLE | GKEY_CR) - // No other flags may be set with this flag. - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - - /** - * @brief Create a keyboard input instance - * - * @param[in] instance The ID of the keyboard input instance (from 0 to 9999) - * - * @return The source handle of the created input instance - */ - GSourceHandle ginputGetKeyboard(uint16_t instance); - - /** - * @brief Get the current keyboard status - * - * @param[in] instance The ID of the keyboard input instance - * @param[in] pkeyboard The keyboard event struct - * - * @return Returns FALSE on an error (eg invalid instance) - */ - bool_t ginputGetKeyboardStatus(uint16_t instance, GEventKeyboard *pkeyboard); - -#ifdef __cplusplus -} -#endif - -#endif /* GINPUT_NEED_KEYBOARD */ - -#endif /* _GINPUT_KEYBOARD_H */ -/** @} */ - diff --git a/src/ginput/mouse.c b/src/ginput/mouse.c deleted file mode 100644 index f7842e33..00000000 --- a/src/ginput/mouse.c +++ /dev/null @@ -1,679 +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/mouse.c - * @brief GINPUT mouse/touch code. - * - * @defgroup Mouse Mouse - * @ingroup GINPUT - * @{ - */ -#include "gfx.h" - -#if (GFX_USE_GINPUT && GINPUT_NEED_MOUSE) || defined(__DOXYGEN__) - -#include "src/ginput/driver_mouse.h" - -#if GINPUT_MOUSE_NEED_CALIBRATION - #if !defined(GFX_USE_GDISP) || !GFX_USE_GDISP - #error "GINPUT: GFX_USE_GDISP must be defined when mouse or touch calibration is required" - #endif - - #include // Required for memcpy - - #define GINPUT_MOUSE_CALIBRATION_FONT "* Double" - #define GINPUT_MOUSE_CALIBRATION_FONT2 "* Narrow" - #define GINPUT_MOUSE_CALIBRATION_TEXT "Calibration" - #define GINPUT_MOUSE_CALIBRATION_ERROR_TEXT "Failed - Please try again!" - #define GINPUT_MOUSE_CALIBRATION_SAME_TEXT "Error: Same Reading - Check Driver!" - - #if GINPUT_MOUSE_MAX_CALIBRATION_ERROR < 0 - #define GINPUT_MOUSE_CALIBRATION_POINTS 3 - #else - #define GINPUT_MOUSE_CALIBRATION_POINTS 4 - #endif - - typedef struct Calibration_t { - float ax; - float bx; - float cx; - float ay; - float by; - float cy; - } Calibration; -#endif - -typedef struct MousePoint_t { - coord_t x, y; -} MousePoint; - -static GTIMER_DECL(MouseTimer); - -static struct MouseConfig_t { - MouseReading t; - MousePoint movepos; - MousePoint clickpos; - systemticks_t clicktime; - uint16_t last_buttons; - uint16_t flags; - #define FLG_INIT_DONE 0x8000 - #define FLG_CLICK_TIMER 0x0001 - #define FLG_IN_CAL 0x0010 - #define FLG_CAL_OK 0x0020 - #define FLG_CAL_SAVED 0x0040 - #define FLG_CAL_FREE 0x0080 - #define FLG_CAL_RAW 0x0100 - #if GINPUT_MOUSE_NEED_CALIBRATION - GMouseCalibrationSaveRoutine fnsavecal; - GMouseCalibrationLoadRoutine fnloadcal; - Calibration caldata; - #endif - GDisplay * display; -} MouseConfig; - -void _tsOrientClip(MouseReading *pt, GDisplay *g, bool_t doClip) { - coord_t w, h; - - w = gdispGGetWidth(g); - h = gdispGGetHeight(g); - - #if GDISP_NEED_CONTROL && !GINPUT_MOUSE_NO_ROTATION - switch(gdispGGetOrientation(g)) { - case GDISP_ROTATE_0: - break; - case GDISP_ROTATE_90: - { - coord_t t = pt->x; - pt->x = w - 1 - pt->y; - pt->y = t; - } - break; - case GDISP_ROTATE_180: - pt->x = w - 1 - pt->x; - pt->y = h - 1 - pt->y; - break; - case GDISP_ROTATE_270: - { - coord_t t = pt->y; - pt->y = h - 1 - pt->x; - pt->x = t; - } - break; - default: - break; - } - #endif - - if (doClip) { - if (pt->x < 0) pt->x = 0; - else if (pt->x >= w) pt->x = w-1; - if (pt->y < 0) pt->y = 0; - else if (pt->y >= h) pt->y = h-1; - } -} - -#if GINPUT_MOUSE_NEED_CALIBRATION - static inline void _tsSetIdentity(Calibration *c) { - c->ax = 1; - c->bx = 0; - c->cx = 0; - c->ay = 0; - c->by = 1; - c->cy = 0; - } - - static inline void _tsDrawCross(const MousePoint *pp) { - gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y, pp->x-2, pp->y, White); - gdispGDrawLine(MouseConfig.display, pp->x+2, pp->y, pp->x+15, pp->y, White); - gdispGDrawLine(MouseConfig.display, pp->x, pp->y-15, pp->x, pp->y-2, White); - gdispGDrawLine(MouseConfig.display, pp->x, pp->y+2, pp->x, pp->y+15, White); - - gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y+15, pp->x-7, pp->y+15, RGB2COLOR(184,158,131)); - gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y+7, pp->x-15, pp->y+15, RGB2COLOR(184,158,131)); - - gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y-15, pp->x-7, pp->y-15, RGB2COLOR(184,158,131)); - gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y-7, pp->x-15, pp->y-15, RGB2COLOR(184,158,131)); - - gdispGDrawLine(MouseConfig.display, pp->x+7, pp->y+15, pp->x+15, pp->y+15, RGB2COLOR(184,158,131)); - gdispGDrawLine(MouseConfig.display, pp->x+15, pp->y+7, pp->x+15, pp->y+15, RGB2COLOR(184,158,131)); - - gdispGDrawLine(MouseConfig.display, pp->x+7, pp->y-15, pp->x+15, pp->y-15, RGB2COLOR(184,158,131)); - gdispGDrawLine(MouseConfig.display, pp->x+15, pp->y-15, pp->x+15, pp->y-7, RGB2COLOR(184,158,131)); - } - - static inline void _tsClearCross(const MousePoint *pp) { - gdispGFillArea(MouseConfig.display, pp->x - 15, pp->y - 15, 42, 42, Blue); - } - - static inline void _tsTransform(MouseReading *pt, const Calibration *c) { - pt->x = (coord_t) (c->ax * pt->x + c->bx * pt->y + c->cx); - pt->y = (coord_t) (c->ay * pt->x + c->by * pt->y + c->cy); - } - - static inline void _tsDo3PointCalibration(const MousePoint *cross, const MousePoint *points, GDisplay *g, Calibration *c) { - float dx; - coord_t c0, c1, c2; - - #if GDISP_NEED_CONTROL - /* Convert all cross points back to GDISP_ROTATE_0 convention - * before calculating the calibration matrix. - */ - switch(gdispGGetOrientation(g)) { - case GDISP_ROTATE_90: - c0 = cross[0].y; - c1 = cross[1].y; - c2 = cross[2].y; - break; - case GDISP_ROTATE_180: - c0 = c1 = c2 = gdispGGetWidth(g) - 1; - c0 -= cross[0].x; - c1 -= cross[1].x; - c2 -= cross[2].x; - break; - case GDISP_ROTATE_270: - c0 = c1 = c2 = gdispGGetHeight(g) - 1; - c0 -= cross[0].y; - c1 -= cross[1].y; - c2 -= cross[2].y; - break; - case GDISP_ROTATE_0: - default: - c0 = cross[0].x; - c1 = cross[1].x; - c2 = cross[2].x; - break; - } - #else - (void) g; - - c0 = cross[0].x; - c1 = cross[1].x; - c2 = cross[2].x; - #endif - - /* Compute all the required determinants */ - dx = (float)(points[0].x - points[2].x) * (float)(points[1].y - points[2].y) - - (float)(points[1].x - points[2].x) * (float)(points[0].y - points[2].y); - - c->ax = ((float)(c0 - c2) * (float)(points[1].y - points[2].y) - - (float)(c1 - c2) * (float)(points[0].y - points[2].y)) / dx; - c->bx = ((float)(c1 - c2) * (float)(points[0].x - points[2].x) - - (float)(c0 - c2) * (float)(points[1].x - points[2].x)) / dx; - c->cx = (c0 * ((float)points[1].x * (float)points[2].y - (float)points[2].x * (float)points[1].y) - - c1 * ((float)points[0].x * (float)points[2].y - (float)points[2].x * (float)points[0].y) - + c2 * ((float)points[0].x * (float)points[1].y - (float)points[1].x * (float)points[0].y)) / dx; - - #if GDISP_NEED_CONTROL - switch(gdispGGetOrientation(g)) { - case GDISP_ROTATE_90: - c0 = c1 = c2 = gdispGGetWidth(g) - 1; - c0 -= cross[0].x; - c1 -= cross[1].x; - c2 -= cross[2].x; - break; - case GDISP_ROTATE_180: - c0 = c1 = c2 = gdispGGetHeight(g) - 1; - c0 -= cross[0].y; - c1 -= cross[1].y; - c2 -= cross[2].y; - break; - case GDISP_ROTATE_270: - c0 = cross[0].x; - c1 = cross[1].x; - c2 = cross[2].x; - break; - case GDISP_ROTATE_0: - default: - c0 = cross[0].y; - c1 = cross[1].y; - c2 = cross[2].y; - break; - } - #else - c0 = cross[0].y; - c1 = cross[1].y; - c2 = cross[2].y; - #endif - - c->ay = ((float)(c0 - c2) * (float)(points[1].y - points[2].y) - - (float)(c1 - c2) * (float)(points[0].y - points[2].y)) / dx; - c->by = ((float)(c1 - c2) * (float)(points[0].x - points[2].x) - - (float)(c0 - c2) * (float)(points[1].x - points[2].x)) / dx; - c->cy = (c0 * ((float)points[1].x * (float)points[2].y - (float)points[2].x * (float)points[1].y) - - c1 * ((float)points[0].x * (float)points[2].y - (float)points[2].x * (float)points[0].y) - + c2 * ((float)points[0].x * (float)points[1].y - (float)points[1].x * (float)points[0].y)) / dx; - } -#endif - -#if GINPUT_MOUSE_READ_CYCLES > 1 - static void get_raw_reading(MouseReading *pt) { - int32_t x, y, z; - unsigned i; - - x = y = z = 0; - for(i = 0; i < GINPUT_MOUSE_READ_CYCLES; i++) { - ginput_lld_mouse_get_reading(pt); - x += pt->x; - y += pt->y; - z += pt->z; - } - - /* Take the average of the readings */ - pt->x = x / GINPUT_MOUSE_READ_CYCLES; - pt->y = y / GINPUT_MOUSE_READ_CYCLES; - pt->z = z / GINPUT_MOUSE_READ_CYCLES; - } -#else - #define get_raw_reading(pt) ginput_lld_mouse_get_reading(pt) -#endif - -static void get_calibrated_reading(MouseReading *pt) { - get_raw_reading(pt); - - #if GINPUT_MOUSE_NEED_CALIBRATION - _tsTransform(pt, &MouseConfig.caldata); - #endif - - _tsOrientClip(pt, MouseConfig.display, !(MouseConfig.flags & FLG_CAL_RAW)); -} - -static void MousePoll(void *param) { - (void) param; - GSourceListener *psl; - GEventMouse *pe; - unsigned meta; - uint16_t upbtns, dnbtns; - uint32_t cdiff; - uint32_t mdiff; - - // Save the last mouse state - MouseConfig.last_buttons = MouseConfig.t.buttons; - - // Get the new mouse reading - get_calibrated_reading(&MouseConfig.t); - - // Calculate out new event meta value and handle CLICK and CXTCLICK - dnbtns = MouseConfig.t.buttons & ~MouseConfig.last_buttons; - upbtns = ~MouseConfig.t.buttons & MouseConfig.last_buttons; - meta = GMETA_NONE; - - // As the touch moves up we need to return a point at the old position because some - // controllers return garbage with the mouse up - if ((upbtns & GINPUT_MOUSE_BTN_LEFT)) { - MouseConfig.t.x = MouseConfig.movepos.x; - MouseConfig.t.y = MouseConfig.movepos.y; - } - - // Calculate the position difference from our movement reference (update the reference if out of range) - mdiff = (MouseConfig.t.x - MouseConfig.movepos.x) * (MouseConfig.t.x - MouseConfig.movepos.x) + - (MouseConfig.t.y - MouseConfig.movepos.y) * (MouseConfig.t.y - MouseConfig.movepos.y); - if (mdiff > GINPUT_MOUSE_MAX_MOVE_JITTER * GINPUT_MOUSE_MAX_MOVE_JITTER) { - MouseConfig.movepos.x = MouseConfig.t.x; - MouseConfig.movepos.y = MouseConfig.t.y; - } - - // Check if the click has moved outside the click area and if so cancel the click - if ((MouseConfig.flags & FLG_CLICK_TIMER)) { - cdiff = (MouseConfig.t.x - MouseConfig.clickpos.x) * (MouseConfig.t.x - MouseConfig.clickpos.x) + - (MouseConfig.t.y - MouseConfig.clickpos.y) * (MouseConfig.t.y - MouseConfig.clickpos.y); - if (cdiff > GINPUT_MOUSE_MAX_CLICK_JITTER * GINPUT_MOUSE_MAX_CLICK_JITTER) - MouseConfig.flags &= ~FLG_CLICK_TIMER; - } - - // Mouse down - if ((dnbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) { - MouseConfig.clickpos.x = MouseConfig.t.x; - MouseConfig.clickpos.y = MouseConfig.t.y; - MouseConfig.clicktime = gfxSystemTicks(); - MouseConfig.flags |= FLG_CLICK_TIMER; - if ((dnbtns & GINPUT_MOUSE_BTN_LEFT)) - meta |= GMETA_MOUSE_DOWN; - } - - // Mouse up - if ((upbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) { - if ((upbtns & GINPUT_MOUSE_BTN_LEFT)) - meta |= GMETA_MOUSE_UP; - if ((MouseConfig.flags & FLG_CLICK_TIMER)) { - if ((upbtns & GINPUT_MOUSE_BTN_LEFT) - #if GINPUT_MOUSE_CLICK_TIME != TIME_INFINITE - && gfxSystemTicks() - MouseConfig.clicktime < gfxMillisecondsToTicks(GINPUT_MOUSE_CLICK_TIME) - #endif - ) - meta |= GMETA_MOUSE_CLICK; - else - meta |= GMETA_MOUSE_CXTCLICK; - MouseConfig.flags &= ~FLG_CLICK_TIMER; - } - } - - // Send the event to the listeners that are interested. - psl = 0; - while ((psl = geventGetSourceListener((GSourceHandle)(&MouseConfig), psl))) { - if (!(pe = (GEventMouse *)geventGetEventBuffer(psl))) { - // This listener is missing - save the meta events that have happened - psl->srcflags |= meta; - continue; - } - - // If we haven't really moved (and there are no meta events) don't bother sending the event - if (mdiff <= GINPUT_MOUSE_MAX_MOVE_JITTER * GINPUT_MOUSE_MAX_MOVE_JITTER && !psl->srcflags - && !meta && MouseConfig.last_buttons == MouseConfig.t.buttons && !(psl->listenflags & GLISTEN_MOUSENOFILTER)) - continue; - - // Send the event if we are listening for it - if (((MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEDOWNMOVES)) - || (!(MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEUPMOVES)) - || (meta && (psl->listenflags & GLISTEN_MOUSEMETA))) { - pe->type = GINPUT_MOUSE_EVENT_TYPE; - pe->instance = 0; - pe->x = MouseConfig.t.x; - pe->y = MouseConfig.t.y; - pe->z = MouseConfig.t.z; - pe->current_buttons = MouseConfig.t.buttons; - pe->last_buttons = MouseConfig.last_buttons; - pe->meta = meta; - if (psl->srcflags) { - pe->current_buttons |= GINPUT_MISSED_MOUSE_EVENT; - pe->meta |= psl->srcflags; - psl->srcflags = 0; - } - pe->display = MouseConfig.display; - geventSendEvent(psl); - } - } -} - -GSourceHandle ginputGetMouse(uint16_t instance) { - #if GINPUT_MOUSE_NEED_CALIBRATION - Calibration *pc; - #endif - - // We only support a single mouse instance currently - // Instance 9999 is the same as instance 0 except that it installs - // a special "raw" calibration if there isn't one we can load. - if (instance && instance != 9999) - return 0; - - // Make sure we have a valid mouse display - if (!MouseConfig.display) - MouseConfig.display = GDISP; - - // Do we need to initialise the mouse subsystem? - if (!(MouseConfig.flags & FLG_INIT_DONE)) { - ginput_lld_mouse_init(); - - #if GINPUT_MOUSE_NEED_CALIBRATION - #if GINPUT_MOUSE_LLD_CALIBRATION_LOADSAVE - if (!MouseConfig.fnloadcal) { - MouseConfig.fnloadcal = ginput_lld_mouse_calibration_load; - MouseConfig.flags &= ~FLG_CAL_FREE; - } - if (!MouseConfig.fnsavecal) - MouseConfig.fnsavecal = ginput_lld_mouse_calibration_save; - #endif - if (MouseConfig.fnloadcal && (pc = (Calibration *)MouseConfig.fnloadcal(instance))) { - memcpy(&MouseConfig.caldata, pc, sizeof(MouseConfig.caldata)); - MouseConfig.flags |= (FLG_CAL_OK|FLG_CAL_SAVED); - if ((MouseConfig.flags & FLG_CAL_FREE)) - gfxFree((void *)pc); - } else if (instance == 9999) { - _tsSetIdentity(&MouseConfig.caldata); - MouseConfig.flags |= (FLG_CAL_OK|FLG_CAL_SAVED|FLG_CAL_RAW); - } else - ginputCalibrateMouse(instance); - #endif - - // Get the first reading - MouseConfig.last_buttons = 0; - get_calibrated_reading(&MouseConfig.t); - - // Mark init as done and start the Poll timer - MouseConfig.flags |= FLG_INIT_DONE; - gtimerStart(&MouseTimer, MousePoll, 0, TRUE, GINPUT_MOUSE_POLL_PERIOD); - } - - // Return our structure as the handle - return (GSourceHandle)&MouseConfig; -} - -void ginputSetMouseDisplay(uint16_t instance, GDisplay *g) { - if (instance) - return; - - MouseConfig.display = g ? g : GDISP; -} - -GDisplay *ginputGetMouseDisplay(uint16_t instance) { - if (instance) - return 0; - - return MouseConfig.display; -} - -bool_t ginputGetMouseStatus(uint16_t instance, GEventMouse *pe) { - // Win32 threads don't seem to recognise priority and/or pre-emption - // so we add a sleep here to prevent 100% polled applications from locking up. - gfxSleepMilliseconds(1); - - if (instance || (MouseConfig.flags & (FLG_INIT_DONE|FLG_IN_CAL)) != FLG_INIT_DONE) - return FALSE; - - pe->type = GINPUT_MOUSE_EVENT_TYPE; - pe->instance = instance; - pe->x = MouseConfig.t.x; - pe->y = MouseConfig.t.y; - pe->z = MouseConfig.t.z; - pe->current_buttons = MouseConfig.t.buttons; - pe->last_buttons = MouseConfig.last_buttons; - if (pe->current_buttons & ~pe->last_buttons & GINPUT_MOUSE_BTN_LEFT) - pe->meta = GMETA_MOUSE_DOWN; - else if (~pe->current_buttons & pe->last_buttons & GINPUT_MOUSE_BTN_LEFT) - pe->meta = GMETA_MOUSE_UP; - else - pe->meta = GMETA_NONE; - return TRUE; -} - -bool_t ginputCalibrateMouse(uint16_t instance) { - #if !GINPUT_MOUSE_NEED_CALIBRATION - (void) instance; - - return FALSE; - #else - - const coord_t height = gdispGGetHeight(MouseConfig.display); - const coord_t width = gdispGGetWidth(MouseConfig.display); - #if GINPUT_MOUSE_CALIBRATE_EXTREMES - const MousePoint cross[] = {{0, 0}, - {(width - 1) , 0}, - {(width - 1) , (height - 1)}, - {(width / 2), (height / 2)}}; /* Check point */ - #else - const MousePoint cross[] = {{(width / 4), (height / 4)}, - {(width - (width / 4)) , (height / 4)}, - {(width - (width / 4)) , (height - (height / 4))}, - {(width / 2), (height / 2)}}; /* Check point */ - #endif - MousePoint points[GINPUT_MOUSE_CALIBRATION_POINTS]; - const MousePoint *pc; - MousePoint *pt; - int32_t px, py; - unsigned i, j; - font_t font1, font2; - #if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0 - unsigned err; - #endif - - if (instance || (MouseConfig.flags & FLG_IN_CAL)) - return FALSE; - - font1 = gdispOpenFont(GINPUT_MOUSE_CALIBRATION_FONT); - font2 = gdispOpenFont(GINPUT_MOUSE_CALIBRATION_FONT2); - - MouseConfig.flags |= FLG_IN_CAL; - gtimerStop(&MouseTimer); - MouseConfig.flags &= ~(FLG_CAL_OK|FLG_CAL_SAVED|FLG_CAL_RAW); - - #if GDISP_NEED_CLIP - gdispGSetClip(MouseConfig.display, 0, 0, width, height); - #endif - - #if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0 - while(1) { - #endif - gdispGClear(MouseConfig.display, Blue); - - gdispGFillStringBox(MouseConfig.display, 0, 5, width, 30, GINPUT_MOUSE_CALIBRATION_TEXT, font1, White, Blue, justifyCenter); - - for(i = 0, pt = points, pc = cross; i < GINPUT_MOUSE_CALIBRATION_POINTS; i++, pt++, pc++) { - _tsDrawCross(pc); - - do { - - /* Wait for the mouse to be pressed */ - while(get_raw_reading(&MouseConfig.t), !(MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT)) - gfxSleepMilliseconds(20); - - /* Average all the samples while the mouse is down */ - for(px = py = 0, j = 0; - gfxSleepMilliseconds(20), /* Settling time between readings */ - get_raw_reading(&MouseConfig.t), - (MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT); - j++) { - px += MouseConfig.t.x; - py += MouseConfig.t.y; - } - - } while(!j); - - pt->x = px / j; - pt->y = py / j; - - _tsClearCross(pc); - - if (i >= 1 && pt->x == (pt-1)->x && pt->y == (pt-1)->y) { - gdispGFillStringBox(MouseConfig.display, 0, 35, width, 40, GINPUT_MOUSE_CALIBRATION_SAME_TEXT, font2, Red, Yellow, justifyCenter); - gfxSleepMilliseconds(5000); - gdispGFillArea(MouseConfig.display, 0, 35, width, 40, Blue); - } - - } - - /* Apply 3 point calibration algorithm */ - _tsDo3PointCalibration(cross, points, MouseConfig.display, &MouseConfig.caldata); - - /* Verification of correctness of calibration (optional) : - * See if the 4th point (Middle of the screen) coincides with the calibrated - * result. If point is within +/- Squareroot(ERROR) pixel margin, then successful calibration - * Else, start from the beginning. - */ - #if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0 - /* Transform the co-ordinates */ - MouseConfig.t.x = points[3].x; - MouseConfig.t.y = points[3].y; - _tsTransform(&MouseConfig.t, &MouseConfig.caldata); - _tsOrientClip(&MouseConfig.t, MouseConfig.display, FALSE); - - /* Calculate the delta */ - err = (MouseConfig.t.x - cross[3].x) * (MouseConfig.t.x - cross[3].x) + - (MouseConfig.t.y - cross[3].y) * (MouseConfig.t.y - cross[3].y); - - if (err <= GINPUT_MOUSE_MAX_CALIBRATION_ERROR * GINPUT_MOUSE_MAX_CALIBRATION_ERROR) - break; - - gdispGFillStringBox(MouseConfig.display, 0, 35, width, 40, GINPUT_MOUSE_CALIBRATION_ERROR_TEXT, font2, Red, Yellow, justifyCenter); - gfxSleepMilliseconds(5000); - } - #endif - - // Restart everything - gdispCloseFont(font1); - gdispCloseFont(font2); - MouseConfig.flags |= FLG_CAL_OK; - MouseConfig.last_buttons = 0; - get_calibrated_reading(&MouseConfig.t); - MouseConfig.flags &= ~FLG_IN_CAL; - if ((MouseConfig.flags & FLG_INIT_DONE)) - gtimerStart(&MouseTimer, MousePoll, 0, TRUE, GINPUT_MOUSE_POLL_PERIOD); - - // Save the calibration data (if possible) - if (MouseConfig.fnsavecal) { - MouseConfig.fnsavecal(instance, (const uint8_t *)&MouseConfig.caldata, sizeof(MouseConfig.caldata)); - MouseConfig.flags |= FLG_CAL_SAVED; - } - - // Clear the screen using the GWIN default background color - #if GFX_USE_GWIN - gdispGClear(MouseConfig.display, gwinGetDefaultBgColor()); - #else - gdispGClear(MouseConfig.display, Black); - #endif - - return TRUE; - #endif -} - -/* Set the routines to save and fetch calibration data. - * This function should be called before first calling ginputGetMouse() for a particular instance - * as the ginputGetMouse() routine may attempt to fetch calibration data and perform a startup calibration if there is no way to get it. - * If this is called after ginputGetMouse() has been called and the driver requires calibration storage, it will immediately save the data is has already obtained. - * The 'requireFree' parameter indicates if the fetch buffer must be free()'d to deallocate the buffer provided by the Fetch routine. - */ -void ginputSetMouseCalibrationRoutines(uint16_t instance, GMouseCalibrationSaveRoutine fnsave, GMouseCalibrationLoadRoutine fnload, bool_t requireFree) { - #if GINPUT_MOUSE_NEED_CALIBRATION - if (instance) - return; - - MouseConfig.fnloadcal = fnload; - MouseConfig.fnsavecal = fnsave; - if (requireFree) - MouseConfig.flags |= FLG_CAL_FREE; - else - MouseConfig.flags &= ~FLG_CAL_FREE; - #if GINPUT_MOUSE_LLD_CALIBRATION_LOADSAVE - if (!MouseConfig.fnloadcal) { - MouseConfig.fnloadcal = ginput_lld_mouse_calibration_load; - MouseConfig.flags &= ~FLG_CAL_FREE; - } - if (!MouseConfig.fnsavecal) - MouseConfig.fnsavecal = ginput_lld_mouse_calibration_save; - #endif - if (MouseConfig.fnsavecal && (MouseConfig.flags & (FLG_CAL_OK|FLG_CAL_SAVED)) == FLG_CAL_OK) { - MouseConfig.fnsavecal(instance, (const uint8_t *)&MouseConfig.caldata, sizeof(MouseConfig.caldata)); - MouseConfig.flags |= FLG_CAL_SAVED; - } - #else - (void)instance, (void)fnsave, (void)fnload, (void)requireFree; - #endif -} - -/* Test if a particular mouse instance requires routines to save its calibration data. */ -bool_t ginputRequireMouseCalibrationStorage(uint16_t instance) { - if (instance) - return FALSE; - - #if GINPUT_MOUSE_NEED_CALIBRATION && !GINPUT_MOUSE_LLD_CALIBRATION_LOADSAVE - return TRUE; - #else - return FALSE; - #endif -} - -/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */ -void ginputMouseWakeup(void) { - gtimerJab(&MouseTimer); -} - -/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */ -void ginputMouseWakeupI(void) { - gtimerJabI(&MouseTimer); -} - -#endif /* GFX_USE_GINPUT && GINPUT_NEED_MOUSE */ -/** @} */ diff --git a/src/ginput/mouse.h b/src/ginput/mouse.h deleted file mode 100644 index 79ad1f08..00000000 --- a/src/ginput/mouse.h +++ /dev/null @@ -1,181 +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/mouse.h - * @brief GINPUT GFX User Input subsystem header file for mouse and touch. - * - * @defgroup Mouse Mouse - * @ingroup GINPUT - * - * @details GINPUT allows it to easily interface touchscreens and mices to - * your application. - * - * @pre GFX_USE_GINPUT must be set to TRUE in your gfxconf.h - * @pre GINPUT_NEED_MOUSE must be set to TRUE in your gfxconf.h - * - * @{ - */ - -#ifndef _GINPUT_MOUSE_H -#define _GINPUT_MOUSE_H - -#if GINPUT_NEED_MOUSE || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -/* This type definition is also used by touch */ -typedef struct GEventMouse_t { - GEventType type; // The type of this event (GEVENT_MOUSE or GEVENT_TOUCH) - uint16_t instance; // The mouse/touch instance - coord_t x, y, z; // The position of the mouse. - // - For touch devices, Z is the current pressure if supported (otherwise 0) - // - For mice, Z is the 3rd dimension if supported (otherwise 0) - uint16_t current_buttons; // A bit is set if the button is down. - // - For touch only bit 0 is relevant - // - For mice the order of the buttons is (from 0 to n) left, right, middle, any other buttons - // - Bit 15 being set indicates that an important mouse event has been missed. - #define GINPUT_MOUSE_BTN_LEFT 0x0001 - #define GINPUT_MOUSE_BTN_RIGHT 0x0002 - #define GINPUT_MOUSE_BTN_MIDDLE 0x0004 - #define GINPUT_MOUSE_BTN_4 0x0008 - #define GINPUT_MISSED_MOUSE_EVENT 0x8000 - #define GINPUT_TOUCH_PRESSED GINPUT_MOUSE_BTN_LEFT - uint16_t last_buttons; // The value of current_buttons on the last event - enum GMouseMeta_e { - GMETA_NONE = 0, // There is no meta event currently happening - GMETA_MOUSE_DOWN = 1, // Button 0 has just gone down - GMETA_MOUSE_UP = 2, // Button 0 has just gone up - GMETA_MOUSE_CLICK = 4, // Button 0 has just gone through a short down - up cycle - GMETA_MOUSE_CXTCLICK = 8 // For mice - The right button has just been depressed - // For touch - a long press has just occurred - } meta; - GDisplay * display; // The display this mouse is currently associated with. - } GEventMouse; - -// Mouse/Touch Listen Flags - passed to geventAddSourceToListener() -#define GLISTEN_MOUSEMETA 0x0001 // Create events for meta events such as CLICK and CXTCLICK -#define GLISTEN_MOUSEDOWNMOVES 0x0002 // Creates mouse move events when the primary mouse button is down (touch is on the surface) -#define GLISTEN_MOUSEUPMOVES 0x0004 // Creates mouse move events when the primary mouse button is up (touch is off the surface - if the hardware allows). -#define GLISTEN_MOUSENOFILTER 0x0008 // Don't filter out mouse moves where the position hasn't changed. -#define GLISTEN_TOUCHMETA GLISTEN_MOUSEMETA -#define GLISTEN_TOUCHDOWNMOVES GLISTEN_MOUSEDOWNMOVES -#define GLISTEN_TOUCHUPMOVES GLISTEN_MOUSEUPMOVES -#define GLISTEN_TOUCHNOFILTER GLISTEN_MOUSENOFILTER - -#define GINPUT_MOUSE_NUM_PORTS 1 // The total number of mouse/touch inputs supported - -// Event types for the mouse ginput source -#define GEVENT_MOUSE (GEVENT_GINPUT_FIRST+0) -#define GEVENT_TOUCH (GEVENT_GINPUT_FIRST+1) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - - /** - * @brief Creates an instance of a mouse and returns the Source handler - * @note HACK: if the instance is 9999, it is treated as instance 0 except - * that no calibration will be performed! - * - * @param[in] instance The ID of the mouse input instance (from 0 to 9999) - * - * @return The source handle of the created instance - */ - GSourceHandle ginputGetMouse(uint16_t instance); - - /** - * @brief Assign the display associated with the mouse - * @note This only needs to be called if the mouse is associated with a display - * other than the current default display. It must be called before - * @p ginputGetMouse() if the new display is to be used during the calibration - * process. Other than calibration the display is used for range checking, - * and may also be used to display a mouse pointer. - * - * @param[in] instance The ID of the mouse input instance - * @param[in] g The GDisplay to which this mouse belongs - */ - void ginputSetMouseDisplay(uint16_t instance, GDisplay *g); - - /** - * @brief Get the display currently associated with the mouse - * @return A pointer to the display - * - * @param[in] instance The ID of the mouse input instance - */ - GDisplay *ginputGetMouseDisplay(uint16_t instance); - - /** - * @brief Get the current mouse position and button status - * @note Unlinke a listener event, this status cannot record meta events such as - * "CLICK". - * - * @param[in] instance The ID of the mouse input instance - * @param[in] pmouse The mouse event - * - * @return FALSE on an error (eg. invalid instance) - */ - bool_t ginputGetMouseStatus(uint16_t instance, GEventMouse *pmouse); - - /** - * @brief Performs a calibration - * - * @param[in] instance The ID of the mouse input instance - * - * @return FALSE if the driver dosen't support a calibration of if the handle is invalid - */ - bool_t ginputCalibrateMouse(uint16_t instance); - - /* Set the routines to save and fetch calibration data. - * This function should be called before first calling ginputGetMouse() for a particular instance - * as the gdispGetMouse() routine may attempt to fetch calibration data and perform a startup calibration if there is no way to get it. - * If this is called after gdispGetMouse() has been called and the driver requires calibration storage, it will immediately save the data is has already obtained. - * The 'requireFree' parameter indicates if the fetch buffer must be free()'d to deallocate the buffer provided by the Fetch routine. - */ - typedef void (*GMouseCalibrationSaveRoutine)(uint16_t instance, const uint8_t *calbuf, size_t sz); // Save calibration data - typedef const char * (*GMouseCalibrationLoadRoutine)(uint16_t instance); // Load calibration data (returns NULL if not data saved) - - /** - * @brief Set the routines to store and restore calibration data - * - * @details This function should be called before first calling ginputGetMouse() for a particular instance - * as the gdispGetMouse() routine may attempt to fetch calibration data and perform a startup calibration if there is no way to get it. - * If this is called after gdispGetMouse() has been called and the driver requires calibration storage, it will immediately save the - * data is has already obtained. - * - * @param[in] instance The ID of the mouse input instance - * @param[in] fnsave The routine to save the data - * @param[in] fnload The routine to restore the data - * @param[in] requireFree TRUE if the buffer returned by the load function must be freed by the mouse code. - */ - void ginputSetMouseCalibrationRoutines(uint16_t instance, GMouseCalibrationSaveRoutine fnsave, GMouseCalibrationLoadRoutine fnload, bool_t requireFree); - - /** - * @brief Test if a particular mouse/touch instance requires routines to save it's alibration data - * @note Not implemented yet - * - * @param[in] instance The ID of the mouse input instance - * - * @return TRUE if needed - */ - bool_t ginputRequireMouseCalibrationStorage(uint16_t instance); - -#ifdef __cplusplus -} -#endif - -#endif /* GINPUT_NEED_MOUSE */ - -#endif /* _GINPUT_MOUSE_H */ -/** @} */ - diff --git a/src/ginput/sys_defs.h b/src/ginput/sys_defs.h index 33259da4..cab1e15d 100644 --- a/src/ginput/sys_defs.h +++ b/src/ginput/sys_defs.h @@ -39,10 +39,10 @@ */ // Include various ginput types -#include "src/ginput/mouse.h" -#include "src/ginput/keyboard.h" -#include "src/ginput/toggle.h" -#include "src/ginput/dial.h" +#include "ginput_mouse.h" +#include "ginput_keyboard.h" +#include "ginput_toggle.h" +#include "ginput_dial.h" #endif /* GFX_USE_GINPUT */ diff --git a/src/ginput/sys_make.mk b/src/ginput/sys_make.mk index 34ac4a55..9abe1645 100644 --- a/src/ginput/sys_make.mk +++ b/src/ginput/sys_make.mk @@ -1,5 +1,5 @@ -GFXSRC += $(GFXLIB)/src/ginput/ginput.c \ - $(GFXLIB)/src/ginput/mouse.c \ - $(GFXLIB)/src/ginput/keyboard.c \ - $(GFXLIB)/src/ginput/toggle.c \ - $(GFXLIB)/src/ginput/dial.c +GFXSRC += $(GFXLIB)/src/ginput/ginput_ginput.c \ + $(GFXLIB)/src/ginput/ginput_mouse.c \ + $(GFXLIB)/src/ginput/ginput_keyboard.c \ + $(GFXLIB)/src/ginput/ginput_toggle.c \ + $(GFXLIB)/src/ginput/ginput_dial.c diff --git a/src/ginput/toggle.c b/src/ginput/toggle.c deleted file mode 100644 index aad9c862..00000000 --- a/src/ginput/toggle.c +++ /dev/null @@ -1,156 +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/toggle.c - * @brief GINPUT toggle code. - * - * @defgroup Toggle Toggle - * @ingroup GINPUT - * @{ - */ -#include "gfx.h" - -#if (GFX_USE_GINPUT && GINPUT_NEED_TOGGLE) || defined(__DOXYGEN__) - -#include "src/ginput/driver_toggle.h" - -#define GINPUT_TOGGLE_ISON 0x01 -#define GINPUT_TOGGLE_INVERT 0x02 - -static GTIMER_DECL(ToggleTimer); -static struct GEventToggleStatus_t { - uint8_t status; -} ToggleStatus[GINPUT_TOGGLE_NUM_PORTS]; - -// Our polling function -static void TogglePoll(void *param) { - (void) param; - - const GToggleConfig *ptc; - GSourceListener *psl; - GEventToggle *pe; - unsigned i, bits, mask; - uint8_t state; - - // Loop while there are bits to get - for(ptc = GInputToggleConfigTable, i=0; i < GINPUT_TOGGLE_NUM_PORTS; ptc++) { - - // Get the next block of bits - bits = ginput_lld_toggle_getbits(ptc) ^ ptc->invert; - - // Extract the bits of use - for(mask = ptc->mask; i < GINPUT_TOGGLE_NUM_PORTS && mask; mask >>= 1, bits >>= 1) { - // Ignore bits not in our mask - if (!(mask & 1)) - continue; - - // Calculate our new state - state = ToggleStatus[i].status & ~GINPUT_TOGGLE_ISON; - if (state & GINPUT_TOGGLE_INVERT) - bits ^= 1; - if (bits & 1) - state |= GINPUT_TOGGLE_ISON; - - // Has it changed? - if ((state ^ ToggleStatus[i].status) & GINPUT_TOGGLE_ISON) { - - // Save the new state - ToggleStatus[i].status = state; - - // Send the event to the listeners that are interested. - psl = 0; - while ((psl = geventGetSourceListener((GSourceHandle)(ToggleStatus+i), psl))) { - if (!(pe = (GEventToggle *)geventGetEventBuffer(psl))) - continue; - if ((state & GINPUT_TOGGLE_ISON)) { - if ((psl->listenflags & GLISTEN_TOGGLE_ON)) { - pe->type = GEVENT_TOGGLE; - pe->instance = i; - pe->on = TRUE; - geventSendEvent(psl); - } - } else { - if ((psl->listenflags & GLISTEN_TOGGLE_OFF)) { - pe->type = GEVENT_TOGGLE; - pe->instance = i; - pe->on = FALSE; - geventSendEvent(psl); - } - } - } - } - - // Next toggle switch - i++; - } - } -} - -/* Hardware Toggle/Switch/Button Functions */ -GSourceHandle ginputGetToggle(uint16_t instance) { - const GToggleConfig *ptc; - - if (instance >= GINPUT_TOGGLE_NUM_PORTS) - return 0; - - // Do we need to initialise the toggle subsystem? - if (!gtimerIsActive(&ToggleTimer)) { - for(ptc = GInputToggleConfigTable; ptc < GInputToggleConfigTable+sizeof(GInputToggleConfigTable)/sizeof(GInputToggleConfigTable[0]); ptc++) - ginput_lld_toggle_init(ptc); - gtimerStart(&ToggleTimer, TogglePoll, 0, TRUE, GINPUT_TOGGLE_POLL_PERIOD); - } - - // OK - return this input - return (GSourceHandle)(ToggleStatus+instance); -} - -// If invert is true, invert the on/off sense for the toggle -void ginputInvertToggle(uint16_t instance, bool_t invert) { - if (instance >= GINPUT_TOGGLE_NUM_PORTS) - return; - if (invert) { - if (!(ToggleStatus[instance].status & GINPUT_TOGGLE_INVERT)) { - ToggleStatus[instance].status |= GINPUT_TOGGLE_INVERT; - ToggleStatus[instance].status ^= GINPUT_TOGGLE_ISON; - } - } else { - if ((ToggleStatus[instance].status & GINPUT_TOGGLE_INVERT)) { - ToggleStatus[instance].status &= ~GINPUT_TOGGLE_INVERT; - ToggleStatus[instance].status ^= GINPUT_TOGGLE_ISON; - } - } -} - -/* Get the current toggle status. - * Returns FALSE on error (eg invalid instance) - */ -bool_t ginputGetToggleStatus(uint16_t instance, GEventToggle *ptoggle) { - // Win32 threads don't seem to recognise priority and/or pre-emption - // so we add a sleep here to prevent 100% polled applications from locking up. - gfxSleepMilliseconds(1); - - if (instance >= GINPUT_TOGGLE_NUM_PORTS) - return FALSE; - ptoggle->type = GEVENT_TOGGLE; - ptoggle->instance = instance; - ptoggle->on = (ToggleStatus[instance].status & GINPUT_TOGGLE_ISON) ? TRUE : FALSE; - return TRUE; -} - -/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */ -void ginputToggleWakeup(void) { - gtimerJab(&ToggleTimer); -} - -/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */ -void ginputToggleWakeupI(void) { - gtimerJabI(&ToggleTimer); -} - -#endif /* GFX_USE_GINPUT && GINPUT_NEED_TOGGLE */ -/** @} */ diff --git a/src/ginput/toggle.h b/src/ginput/toggle.h deleted file mode 100644 index 40149754..00000000 --- a/src/ginput/toggle.h +++ /dev/null @@ -1,100 +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/toggle.h - * @brief GINPUT GFX User Input subsystem header file. - * - * @defgroup Toggle Toggle - * @ingroup GINPUT - * - * @details GINPUT allows it to interface toggle buttons easily to your - * application. - * - * @pre GFX_USE_GINPUT must be set to TRUE in your gfxconf.h - * @pre GINPUT_NEED_TOGGLE must be set to TRUE in your gfxconf.h - * - * @{ - */ - -#ifndef _GINPUT_TOGGLE_H -#define _GINPUT_TOGGLE_H - -#if GINPUT_NEED_TOGGLE || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Low Level Driver details and error checks. */ -/*===========================================================================*/ - -// Get the hardware definitions - Number of instances etc. -#include "ginput_lld_toggle_config.h" - -#ifndef GINPUT_TOGGLE_POLL_PERIOD - #define GINPUT_TOGGLE_POLL_PERIOD 200 -#endif - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -// Event types for various ginput sources -#define GEVENT_TOGGLE (GEVENT_GINPUT_FIRST+3) - -typedef struct GEventToggle_t { - GEventType type; // The type of this event (GEVENT_TOGGLE) - uint16_t instance; // The toggle instance - bool_t on; // True if the toggle/button is on - } GEventToggle; - -// Toggle Listen Flags - passed to geventAddSourceToListener() -#define GLISTEN_TOGGLE_ON 0x0001 // Return an event when the toggle turns on -#define GLISTEN_TOGGLE_OFF 0x0002 // Return an event when the toggle turns off - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - - /** - * @brief Create a toggle input instance - * - * @param[in] instance The ID of the toggle input instance (from 0 to 9999) - * - * @return The source handle of the created instance - */ - GSourceHandle ginputGetToggle(uint16_t instance); - - /** - * @brief Can be used to invert the sense of a toggle - * - * @param[in] instance The ID of the toggle input instance - * @param[in] invert If TRUE, will be inverted - */ - void ginputInvertToggle(uint16_t instance, bool_t invert); - - /** - * @brief Get the current toggle status - * - * @param[in] instance The ID of the toggle input instance - * @param[in] ptoggle The toggle event struct - * - * @return Returns FALSE on an error (eg invalid instance) - */ - bool_t ginputGetToggleStatus(uint16_t instance, GEventToggle *ptoggle); - -#ifdef __cplusplus -} -#endif - -#endif /* GINPUT_NEED_TOGGLE */ - -#endif /* _GINPUT_TOGGLE_H */ -/** @} */ - diff --git a/src/gmisc/arrayops.c b/src/gmisc/arrayops.c deleted file mode 100644 index 1c4f821a..00000000 --- a/src/gmisc/arrayops.c +++ /dev/null @@ -1,226 +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/arrayops.c - * @brief GMISC Array Operations code. - * - * @addtogroup GMISC - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GMISC && GMISC_NEED_ARRAYOPS - -void gmiscArrayConvert(ArrayDataFormat srcfmt, void *src, ArrayDataFormat dstfmt, void *dst, size_t cnt) { - uint8_t *src8, *dst8; - uint16_t *src16, *dst16; - - dst8 = dst; - dst16 = dst; - src8 = src; - src16 = src; - - /* We do this as a big switch in order to optimise efficiency for each transfer type */ - switch(dstfmt) { - case ARRAY_DATA_4BITUNSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: if (dst != src) while(cnt--) { *dst8++ = *src8++; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 8); } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst8++ = *src8++ >> 4; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 128) >> 4; } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 6; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 512) >> 6; } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 8; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 2048) >> 8; } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 10; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 8192) >> 10; } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 12; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 32768) >> 12; } break; - } - break; - case ARRAY_DATA_4BITSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 8); } break; - case ARRAY_DATA_4BITSIGNED: if (dst != src) while(cnt--) { *dst8++ = *src8++; } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 128) >> 4; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst8++ = *src8++ >> 4; } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 512) >> 6; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 6; } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 2048) >> 8; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 8; } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 8192) >> 10; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 10; } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 32768) >> 12; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 12; } break; - } - break; - case ARRAY_DATA_8BITUNSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst8++ = *src8++ << 4; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 8) << 4; } break; - case ARRAY_DATA_8BITUNSIGNED: if (dst != src) while(cnt--) { *dst8++ = *src8++; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 128); } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 2; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 512) >> 2; } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 4; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 2048) >> 4; } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 6; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 8192) >> 6; } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 8; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 32768) >> 8; } break; - } - break; - case ARRAY_DATA_8BITSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 8) << 4; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst8++ = *src8++ << 4; } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 128); } break; - case ARRAY_DATA_8BITSIGNED: if (dst != src) while(cnt--) { *dst8++ = *src8++; } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 512) >> 2; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 2; } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 2048) >> 4; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 4; } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 8192) >> 6; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 6; } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 32768) >> 8; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 8; } break; - } - break; - case ARRAY_DATA_10BITUNSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 6; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 6; } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 2; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 2; } break; - case ARRAY_DATA_10BITUNSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512); } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) >> 2; } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 4; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) >> 4; } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 6; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 6; } break; - } - break; - case ARRAY_DATA_10BITSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 6; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 6; } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 2; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 2; } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512); } break; - case ARRAY_DATA_10BITSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) >> 2; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) >> 4; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 4; } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 6; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 6; } break; - } - break; - case ARRAY_DATA_12BITUNSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 8; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 8; } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 4; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 4; } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ << 2; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 2; } break; - case ARRAY_DATA_12BITUNSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048); } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) >> 2; } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 4; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 4; } break; - } - break; - case ARRAY_DATA_12BITSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 8; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 8; } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 4; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 4; } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 2; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = *src16++ << 2; } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048); } break; - case ARRAY_DATA_12BITSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) >> 2; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 4; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 4; } break; - } - break; - case ARRAY_DATA_14BITUNSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 10; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 10; } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 6; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 6; } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ << 4; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 4; } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ << 2; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) >> 2; } break; - case ARRAY_DATA_14BITUNSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192); } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 2; } break; - } - break; - case ARRAY_DATA_14BITSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 10; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 10; } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 6; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 6; } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 4; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = *src16++ << 4; } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) << 2; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = *src16++ << 2; } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192); } break; - case ARRAY_DATA_14BITSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 2; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; - } - break; - case ARRAY_DATA_16BITUNSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 12; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 12; } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 8; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 8; } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ << 6; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 6; } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ << 4; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) >> 4; } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) >> 2; } break; - case ARRAY_DATA_16BITUNSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; - case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768); } break; - } - break; - case ARRAY_DATA_16BITSIGNED: - switch(srcfmt) { - case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 12; } break; - case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 12; } break; - case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 8; } break; - case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 8; } break; - case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 6; } break; - case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = *src16++ << 6; } break; - case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) << 4; } break; - case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = *src16++ << 4; } break; - case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) << 2; } break; - case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; - case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768); } break; - case ARRAY_DATA_16BITSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; - } - break; - } -} - -#endif /* GFX_USE_GMISC && GMISC_NEED_ARRAYOPS */ -/** @} */ diff --git a/src/gmisc/gmisc.c b/src/gmisc/gmisc.c deleted file mode 100644 index 654dd5e2..00000000 --- a/src/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.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_arrayops.c b/src/gmisc/gmisc_arrayops.c new file mode 100644 index 00000000..6e5442cd --- /dev/null +++ b/src/gmisc/gmisc_arrayops.c @@ -0,0 +1,226 @@ +/* + * 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_arrayops.c + * @brief GMISC Array Operations code. + * + * @addtogroup GMISC + * @{ + */ +#include "gfx.h" + +#if GFX_USE_GMISC && GMISC_NEED_ARRAYOPS + +void gmiscArrayConvert(ArrayDataFormat srcfmt, void *src, ArrayDataFormat dstfmt, void *dst, size_t cnt) { + uint8_t *src8, *dst8; + uint16_t *src16, *dst16; + + dst8 = dst; + dst16 = dst; + src8 = src; + src16 = src; + + /* We do this as a big switch in order to optimise efficiency for each transfer type */ + switch(dstfmt) { + case ARRAY_DATA_4BITUNSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: if (dst != src) while(cnt--) { *dst8++ = *src8++; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 8); } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst8++ = *src8++ >> 4; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 128) >> 4; } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 6; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 512) >> 6; } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 8; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 2048) >> 8; } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 10; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 8192) >> 10; } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 12; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 32768) >> 12; } break; + } + break; + case ARRAY_DATA_4BITSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 8); } break; + case ARRAY_DATA_4BITSIGNED: if (dst != src) while(cnt--) { *dst8++ = *src8++; } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 128) >> 4; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst8++ = *src8++ >> 4; } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 512) >> 6; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 6; } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 2048) >> 8; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 8; } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 8192) >> 10; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 10; } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 32768) >> 12; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 12; } break; + } + break; + case ARRAY_DATA_8BITUNSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst8++ = *src8++ << 4; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 8) << 4; } break; + case ARRAY_DATA_8BITUNSIGNED: if (dst != src) while(cnt--) { *dst8++ = *src8++; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 128); } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 2; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 512) >> 2; } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 4; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 2048) >> 4; } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 6; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 8192) >> 6; } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst8++ = *src16++ >> 8; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 32768) >> 8; } break; + } + break; + case ARRAY_DATA_8BITSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 8) << 4; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst8++ = *src8++ << 4; } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst8++ = (*src8++ ^ 128); } break; + case ARRAY_DATA_8BITSIGNED: if (dst != src) while(cnt--) { *dst8++ = *src8++; } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 512) >> 2; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 2; } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 2048) >> 4; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 4; } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 8192) >> 6; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 6; } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst8++ = (*src16++ ^ 32768) >> 8; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst8++ = *src16++ >> 8; } break; + } + break; + case ARRAY_DATA_10BITUNSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 6; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 6; } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 2; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 2; } break; + case ARRAY_DATA_10BITUNSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512); } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) >> 2; } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 4; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) >> 4; } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 6; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 6; } break; + } + break; + case ARRAY_DATA_10BITSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 6; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 6; } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 2; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 2; } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512); } break; + case ARRAY_DATA_10BITSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) >> 2; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) >> 4; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 4; } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 6; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 6; } break; + } + break; + case ARRAY_DATA_12BITUNSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 8; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 8; } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 4; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 4; } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ << 2; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 2; } break; + case ARRAY_DATA_12BITUNSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048); } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) >> 2; } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 4; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 4; } break; + } + break; + case ARRAY_DATA_12BITSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 8; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 8; } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 4; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 4; } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 2; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = *src16++ << 2; } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048); } break; + case ARRAY_DATA_12BITSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) >> 2; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 4; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 4; } break; + } + break; + case ARRAY_DATA_14BITUNSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 10; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 10; } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 6; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 6; } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ << 4; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 4; } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ << 2; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) >> 2; } break; + case ARRAY_DATA_14BITUNSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192); } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 2; } break; + } + break; + case ARRAY_DATA_14BITSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 10; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 10; } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 6; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 6; } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 4; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = *src16++ << 4; } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) << 2; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = *src16++ << 2; } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192); } break; + case ARRAY_DATA_14BITSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768) >> 2; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; + } + break; + case ARRAY_DATA_16BITUNSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 12; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 12; } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = *src8++ << 8; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 8; } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ << 6; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 6; } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ << 4; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) >> 4; } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) >> 2; } break; + case ARRAY_DATA_16BITUNSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; + case ARRAY_DATA_16BITSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768); } break; + } + break; + case ARRAY_DATA_16BITSIGNED: + switch(srcfmt) { + case ARRAY_DATA_4BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 8) << 12; } break; + case ARRAY_DATA_4BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 12; } break; + case ARRAY_DATA_8BITUNSIGNED: while(cnt--) { *dst16++ = (*src8++ ^ 128) << 8; } break; + case ARRAY_DATA_8BITSIGNED: while(cnt--) { *dst16++ = *src8++ << 8; } break; + case ARRAY_DATA_10BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 512) << 6; } break; + case ARRAY_DATA_10BITSIGNED: while(cnt--) { *dst16++ = *src16++ << 6; } break; + case ARRAY_DATA_12BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 2048) << 4; } break; + case ARRAY_DATA_12BITSIGNED: while(cnt--) { *dst16++ = *src16++ << 4; } break; + case ARRAY_DATA_14BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 8192) << 2; } break; + case ARRAY_DATA_14BITSIGNED: while(cnt--) { *dst16++ = *src16++ >> 2; } break; + case ARRAY_DATA_16BITUNSIGNED: while(cnt--) { *dst16++ = (*src16++ ^ 32768); } break; + case ARRAY_DATA_16BITSIGNED: if (dst != src) while(cnt--) { *dst16++ = *src16++; } break; + } + break; + } +} + +#endif /* GFX_USE_GMISC && GMISC_NEED_ARRAYOPS */ +/** @} */ diff --git a/src/gmisc/gmisc_gmisc.c b/src/gmisc/gmisc_gmisc.c new file mode 100644 index 00000000..3121182f --- /dev/null +++ b/src/gmisc/gmisc_gmisc.c @@ -0,0 +1,27 @@ +/* + * 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_trig.c b/src/gmisc/gmisc_trig.c new file mode 100644 index 00000000..7314f389 --- /dev/null +++ b/src/gmisc/gmisc_trig.c @@ -0,0 +1,190 @@ +/* + * 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_trig.c + * @brief GMISC Trig Functions. + * + * @addtogroup GMISC + * @{ + */ +#include "gfx.h" + +#if GFX_USE_GMISC + +#if GMISC_NEED_FASTTRIG + const double sintabledouble[] = { + 0.000000, 0.017452, 0.034899, 0.052336, 0.069756, 0.087156, 0.104528, 0.121869, + 0.139173, 0.156434, 0.173648, 0.190809, 0.207912, 0.224951, 0.241922, 0.258819, + 0.275637, 0.292372, 0.309017, 0.325568, 0.342020, 0.358368, 0.374607, 0.390731, + 0.406737, 0.422618, 0.438371, 0.453990, 0.469472, 0.484810, 0.500000, 0.515038, + 0.529919, 0.544639, 0.559193, 0.573576, 0.587785, 0.601815, 0.615661, 0.629320, + 0.642788, 0.656059, 0.669131, 0.681998, 0.694658, 0.707107, 0.719340, 0.731354, + 0.743145, 0.754710, 0.766044, 0.777146, 0.788011, 0.798636, 0.809017, 0.819152, + 0.829038, 0.838671, 0.848048, 0.857167, 0.866025, 0.874620, 0.882948, 0.891007, + 0.898794, 0.906308, 0.913545, 0.920505, 0.927184, 0.933580, 0.939693, 0.945519, + 0.951057, 0.956305, 0.961262, 0.965926, 0.970296, 0.974370, 0.978148, 0.981627, + 0.984808, 0.987688, 0.990268, 0.992546, 0.994522, 0.996195, 0.997564, 0.998630, + 0.999391, 0.999848, 1.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195, + 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, 0.978148, 0.974370, + 0.970296, 0.965926, 0.961262, 0.956305, 0.951057, 0.945519, 0.939693, 0.933580, + 0.927184, 0.920505, 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620, + 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, 0.809017, 0.798636, + 0.788011, 0.777146, 0.766044, 0.754710, 0.743145, 0.731354, 0.719340, 0.707107, + 0.694658, 0.681998, 0.669131, 0.656059, 0.642788, 0.629320, 0.615661, 0.601815, + 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, 0.500000, 0.484810, + 0.469472, 0.453990, 0.438371, 0.422618, 0.406737, 0.390731, 0.374607, 0.358368, + 0.342020, 0.325568, 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, + 0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869, 0.104528, 0.087156, + 0.069756, 0.052336, 0.034899, 0.017452, 0.000000, -0.017452, -0.034899, -0.052336, + -0.069756, -0.087156, -0.104528, -0.121869, -0.139173, -0.156434, -0.173648, -0.190809, + -0.207912, -0.224951, -0.241922, -0.258819, -0.275637, -0.292372, -0.309017, -0.325568, + -0.342020, -0.358368, -0.374607, -0.390731, -0.406737, -0.422618, -0.438371, -0.453990, + -0.469472, -0.484810, -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573576, + -0.587785, -0.601815, -0.615661, -0.629320, -0.642788, -0.656059, -0.669131, -0.681998, + -0.694658, -0.707107, -0.719340, -0.731354, -0.743145, -0.754710, -0.766044, -0.777146, + -0.788011, -0.798636, -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167, + -0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308, -0.913545, -0.920505, + -0.927184, -0.933580, -0.939693, -0.945519, -0.951057, -0.956305, -0.961262, -0.965926, + -0.970296, -0.974370, -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546, + -0.994522, -0.996195, -0.997564, -0.998630, -0.999391, -0.999848, -1.000000, -0.999848, + -0.999391, -0.998630, -0.997564, -0.996195, -0.994522, -0.992546, -0.990268, -0.987688, + -0.984808, -0.981627, -0.978148, -0.974370, -0.970296, -0.965926, -0.961262, -0.956305, + -0.951057, -0.945519, -0.939693, -0.933580, -0.927184, -0.920505, -0.913545, -0.906308, + -0.898794, -0.891007, -0.882948, -0.874620, -0.866025, -0.857167, -0.848048, -0.838671, + -0.829038, -0.819152, -0.809017, -0.798636, -0.788011, -0.777146, -0.766044, -0.754710, + -0.743145, -0.731354, -0.719340, -0.707107, -0.694658, -0.681998, -0.669131, -0.656059, + -0.642788, -0.629320, -0.615661, -0.601815, -0.587785, -0.573576, -0.559193, -0.544639, + -0.529919, -0.515038, -0.500000, -0.484810, -0.469472, -0.453990, -0.438371, -0.422618, + -0.406737, -0.390731, -0.374607, -0.358368, -0.342020, -0.325568, -0.309017, -0.292372, + -0.275637, -0.258819, -0.241922, -0.224951, -0.207912, -0.190809, -0.173648, -0.156434, + -0.139173, -0.121869, -0.104528, -0.087156, -0.069756, -0.052336, -0.034899, -0.017452 + }; + + double fsin(int degrees) { + if (degrees < 0) + degrees -= (degrees/360-1)*360; + else if (degrees >= 360) + degrees %= 360; + return sintabledouble[degrees]; + } + + double fcos(int degrees) { + return fsin(degrees+90); + } + +#endif + +#if GMISC_NEED_FIXEDTRIG + const fixed sintablefixed[] = { + 0, 1143, 2287, 3429, 4571, 5711, 6850, 7986, + 9120, 10252, 11380, 12504, 13625, 14742, 15854, 16961, + 18064, 19160, 20251, 21336, 22414, 23486, 24550, 25606, + 26655, 27696, 28729, 29752, 30767, 31772, 32767, 33753, + 34728, 35693, 36647, 37589, 38521, 39440, 40347, 41243, + 42125, 42995, 43852, 44695, 45525, 46340, 47142, 47929, + 48702, 49460, 50203, 50931, 51643, 52339, 53019, 53683, + 54331, 54963, 55577, 56175, 56755, 57319, 57864, 58393, + 58903, 59395, 59870, 60326, 60763, 61183, 61583, 61965, + 62328, 62672, 62997, 63302, 63589, 63856, 64103, 64331, + 64540, 64729, 64898, 65047, 65176, 65286, 65376, 65446, + 65496, 65526, 65536, 65526, 65496, 65446, 65376, 65286, + 65176, 65047, 64898, 64729, 64540, 64331, 64103, 63856, + 63589, 63302, 62997, 62672, 62328, 61965, 61583, 61183, + 60763, 60326, 59870, 59395, 58903, 58393, 57864, 57319, + 56755, 56175, 55577, 54963, 54331, 53683, 53019, 52339, + 51643, 50931, 50203, 49460, 48702, 47929, 47142, 46340, + 45525, 44695, 43852, 42995, 42125, 41243, 40347, 39440, + 38521, 37589, 36647, 35693, 34728, 33753, 32767, 31772, + 30767, 29752, 28729, 27696, 26655, 25606, 24550, 23486, + 22414, 21336, 20251, 19160, 18064, 16961, 15854, 14742, + 13625, 12504, 11380, 10252, 9120, 7986, 6850, 5711, + 4571, 3429, 2287, 1143, 0, -1143, -2287, -3429, + -4571, -5711, -6850, -7986, -9120, -10252, -11380, -12504, + -13625, -14742, -15854, -16961, -18064, -19160, -20251, -21336, + -22414, -23486, -24550, -25606, -26655, -27696, -28729, -29752, + -30767, -31772, -32768, -33753, -34728, -35693, -36647, -37589, + -38521, -39440, -40347, -41243, -42125, -42995, -43852, -44695, + -45525, -46340, -47142, -47929, -48702, -49460, -50203, -50931, + -51643, -52339, -53019, -53683, -54331, -54963, -55577, -56175, + -56755, -57319, -57864, -58393, -58903, -59395, -59870, -60326, + -60763, -61183, -61583, -61965, -62328, -62672, -62997, -63302, + -63589, -63856, -64103, -64331, -64540, -64729, -64898, -65047, + -65176, -65286, -65376, -65446, -65496, -65526, -65536, -65526, + -65496, -65446, -65376, -65286, -65176, -65047, -64898, -64729, + -64540, -64331, -64103, -63856, -63589, -63302, -62997, -62672, + -62328, -61965, -61583, -61183, -60763, -60326, -59870, -59395, + -58903, -58393, -57864, -57319, -56755, -56175, -55577, -54963, + -54331, -53683, -53019, -52339, -51643, -50931, -50203, -49460, + -48702, -47929, -47142, -46340, -45525, -44695, -43852, -42995, + -42125, -41243, -40347, -39440, -38521, -37589, -36647, -35693, + -34728, -33753, -32768, -31772, -30767, -29752, -28729, -27696, + -26655, -25606, -24550, -23486, -22414, -21336, -20251, -19160, + -18064, -16961, -15854, -14742, -13625, -12504, -11380, -10252, + -9120, -7986, -6850, -5711, -4571, -3429, -2287, -1143 + }; + + fixed ffsin(int degrees) { + if (degrees < 0) + degrees -= (degrees/360-1)*360; + else if (degrees >= 360) + degrees %= 360; + return sintablefixed[degrees]; + } + + fixed ffcos(int degrees) { + return ffsin(degrees+90); + } + +#endif + +#if GMISC_NEED_INVSQRT + // Algorithm based on Quake code. + #if GMISC_INVSQRT_REAL_SLOW + #include + float invsqrt(float n) { + return 1.0/sqrt(n); + } + #else + float invsqrt(float n) { + int32_t i; + float x2; + + x2 = n * 0.5F; + + // Convert into an int32 (no binary format conversion) + #if GMISC_INVSQRT_MIXED_ENDIAN + ((char *)&i)[0] = ((char *)&n)[3]; + ((char *)&i)[1] = ((char *)&n)[2]; + ((char *)&i)[2] = ((char *)&n)[1]; + ((char *)&i)[3] = ((char *)&n)[0]; + #else + i = *(int32_t *)&n; + #endif + + // evil floating point bit level hacking + i = 0x5F375A86 - (i >> 1); // The quake code used 0x5F3759DF but this is better. + + // Convert back to a float (no binary format conversion) + #if GMISC_INVSQRT_MIXED_ENDIAN + ((char *)&n)[0] = ((char *)&i)[3]; + ((char *)&n)[1] = ((char *)&i)[2]; + ((char *)&n)[2] = ((char *)&i)[1]; + ((char *)&n)[3] = ((char *)&i)[0]; + #else + n = *(float *)&i; + #endif + + n = n * (1.5F - (x2 * n * n)); // 1st iteration + //n = n * (1.5F - (x2 * n * n)); // 2nd iteration for extra precision, this can be removed + return n; + } + #endif +#endif + +#endif /* GFX_USE_GMISC */ +/** @} */ diff --git a/src/gmisc/sys_make.mk b/src/gmisc/sys_make.mk index 66d19ab0..0431c134 100644 --- a/src/gmisc/sys_make.mk +++ b/src/gmisc/sys_make.mk @@ -1,3 +1,3 @@ -GFXSRC += $(GFXLIB)/src/gmisc/gmisc.c \ - $(GFXLIB)/src/gmisc/arrayops.c \ - $(GFXLIB)/src/gmisc/trig.c +GFXSRC += $(GFXLIB)/src/gmisc/gmisc_gmisc.c \ + $(GFXLIB)/src/gmisc/gmisc_arrayops.c \ + $(GFXLIB)/src/gmisc/gmisc_trig.c diff --git a/src/gmisc/trig.c b/src/gmisc/trig.c deleted file mode 100644 index 3c6dd461..00000000 --- a/src/gmisc/trig.c +++ /dev/null @@ -1,190 +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/trig.c - * @brief GMISC Trig Functions. - * - * @addtogroup GMISC - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GMISC - -#if GMISC_NEED_FASTTRIG - const double sintabledouble[] = { - 0.000000, 0.017452, 0.034899, 0.052336, 0.069756, 0.087156, 0.104528, 0.121869, - 0.139173, 0.156434, 0.173648, 0.190809, 0.207912, 0.224951, 0.241922, 0.258819, - 0.275637, 0.292372, 0.309017, 0.325568, 0.342020, 0.358368, 0.374607, 0.390731, - 0.406737, 0.422618, 0.438371, 0.453990, 0.469472, 0.484810, 0.500000, 0.515038, - 0.529919, 0.544639, 0.559193, 0.573576, 0.587785, 0.601815, 0.615661, 0.629320, - 0.642788, 0.656059, 0.669131, 0.681998, 0.694658, 0.707107, 0.719340, 0.731354, - 0.743145, 0.754710, 0.766044, 0.777146, 0.788011, 0.798636, 0.809017, 0.819152, - 0.829038, 0.838671, 0.848048, 0.857167, 0.866025, 0.874620, 0.882948, 0.891007, - 0.898794, 0.906308, 0.913545, 0.920505, 0.927184, 0.933580, 0.939693, 0.945519, - 0.951057, 0.956305, 0.961262, 0.965926, 0.970296, 0.974370, 0.978148, 0.981627, - 0.984808, 0.987688, 0.990268, 0.992546, 0.994522, 0.996195, 0.997564, 0.998630, - 0.999391, 0.999848, 1.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195, - 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, 0.978148, 0.974370, - 0.970296, 0.965926, 0.961262, 0.956305, 0.951057, 0.945519, 0.939693, 0.933580, - 0.927184, 0.920505, 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620, - 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, 0.809017, 0.798636, - 0.788011, 0.777146, 0.766044, 0.754710, 0.743145, 0.731354, 0.719340, 0.707107, - 0.694658, 0.681998, 0.669131, 0.656059, 0.642788, 0.629320, 0.615661, 0.601815, - 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, 0.500000, 0.484810, - 0.469472, 0.453990, 0.438371, 0.422618, 0.406737, 0.390731, 0.374607, 0.358368, - 0.342020, 0.325568, 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, - 0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869, 0.104528, 0.087156, - 0.069756, 0.052336, 0.034899, 0.017452, 0.000000, -0.017452, -0.034899, -0.052336, - -0.069756, -0.087156, -0.104528, -0.121869, -0.139173, -0.156434, -0.173648, -0.190809, - -0.207912, -0.224951, -0.241922, -0.258819, -0.275637, -0.292372, -0.309017, -0.325568, - -0.342020, -0.358368, -0.374607, -0.390731, -0.406737, -0.422618, -0.438371, -0.453990, - -0.469472, -0.484810, -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573576, - -0.587785, -0.601815, -0.615661, -0.629320, -0.642788, -0.656059, -0.669131, -0.681998, - -0.694658, -0.707107, -0.719340, -0.731354, -0.743145, -0.754710, -0.766044, -0.777146, - -0.788011, -0.798636, -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167, - -0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308, -0.913545, -0.920505, - -0.927184, -0.933580, -0.939693, -0.945519, -0.951057, -0.956305, -0.961262, -0.965926, - -0.970296, -0.974370, -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546, - -0.994522, -0.996195, -0.997564, -0.998630, -0.999391, -0.999848, -1.000000, -0.999848, - -0.999391, -0.998630, -0.997564, -0.996195, -0.994522, -0.992546, -0.990268, -0.987688, - -0.984808, -0.981627, -0.978148, -0.974370, -0.970296, -0.965926, -0.961262, -0.956305, - -0.951057, -0.945519, -0.939693, -0.933580, -0.927184, -0.920505, -0.913545, -0.906308, - -0.898794, -0.891007, -0.882948, -0.874620, -0.866025, -0.857167, -0.848048, -0.838671, - -0.829038, -0.819152, -0.809017, -0.798636, -0.788011, -0.777146, -0.766044, -0.754710, - -0.743145, -0.731354, -0.719340, -0.707107, -0.694658, -0.681998, -0.669131, -0.656059, - -0.642788, -0.629320, -0.615661, -0.601815, -0.587785, -0.573576, -0.559193, -0.544639, - -0.529919, -0.515038, -0.500000, -0.484810, -0.469472, -0.453990, -0.438371, -0.422618, - -0.406737, -0.390731, -0.374607, -0.358368, -0.342020, -0.325568, -0.309017, -0.292372, - -0.275637, -0.258819, -0.241922, -0.224951, -0.207912, -0.190809, -0.173648, -0.156434, - -0.139173, -0.121869, -0.104528, -0.087156, -0.069756, -0.052336, -0.034899, -0.017452 - }; - - double fsin(int degrees) { - if (degrees < 0) - degrees -= (degrees/360-1)*360; - else if (degrees >= 360) - degrees %= 360; - return sintabledouble[degrees]; - } - - double fcos(int degrees) { - return fsin(degrees+90); - } - -#endif - -#if GMISC_NEED_FIXEDTRIG - const fixed sintablefixed[] = { - 0, 1143, 2287, 3429, 4571, 5711, 6850, 7986, - 9120, 10252, 11380, 12504, 13625, 14742, 15854, 16961, - 18064, 19160, 20251, 21336, 22414, 23486, 24550, 25606, - 26655, 27696, 28729, 29752, 30767, 31772, 32767, 33753, - 34728, 35693, 36647, 37589, 38521, 39440, 40347, 41243, - 42125, 42995, 43852, 44695, 45525, 46340, 47142, 47929, - 48702, 49460, 50203, 50931, 51643, 52339, 53019, 53683, - 54331, 54963, 55577, 56175, 56755, 57319, 57864, 58393, - 58903, 59395, 59870, 60326, 60763, 61183, 61583, 61965, - 62328, 62672, 62997, 63302, 63589, 63856, 64103, 64331, - 64540, 64729, 64898, 65047, 65176, 65286, 65376, 65446, - 65496, 65526, 65536, 65526, 65496, 65446, 65376, 65286, - 65176, 65047, 64898, 64729, 64540, 64331, 64103, 63856, - 63589, 63302, 62997, 62672, 62328, 61965, 61583, 61183, - 60763, 60326, 59870, 59395, 58903, 58393, 57864, 57319, - 56755, 56175, 55577, 54963, 54331, 53683, 53019, 52339, - 51643, 50931, 50203, 49460, 48702, 47929, 47142, 46340, - 45525, 44695, 43852, 42995, 42125, 41243, 40347, 39440, - 38521, 37589, 36647, 35693, 34728, 33753, 32767, 31772, - 30767, 29752, 28729, 27696, 26655, 25606, 24550, 23486, - 22414, 21336, 20251, 19160, 18064, 16961, 15854, 14742, - 13625, 12504, 11380, 10252, 9120, 7986, 6850, 5711, - 4571, 3429, 2287, 1143, 0, -1143, -2287, -3429, - -4571, -5711, -6850, -7986, -9120, -10252, -11380, -12504, - -13625, -14742, -15854, -16961, -18064, -19160, -20251, -21336, - -22414, -23486, -24550, -25606, -26655, -27696, -28729, -29752, - -30767, -31772, -32768, -33753, -34728, -35693, -36647, -37589, - -38521, -39440, -40347, -41243, -42125, -42995, -43852, -44695, - -45525, -46340, -47142, -47929, -48702, -49460, -50203, -50931, - -51643, -52339, -53019, -53683, -54331, -54963, -55577, -56175, - -56755, -57319, -57864, -58393, -58903, -59395, -59870, -60326, - -60763, -61183, -61583, -61965, -62328, -62672, -62997, -63302, - -63589, -63856, -64103, -64331, -64540, -64729, -64898, -65047, - -65176, -65286, -65376, -65446, -65496, -65526, -65536, -65526, - -65496, -65446, -65376, -65286, -65176, -65047, -64898, -64729, - -64540, -64331, -64103, -63856, -63589, -63302, -62997, -62672, - -62328, -61965, -61583, -61183, -60763, -60326, -59870, -59395, - -58903, -58393, -57864, -57319, -56755, -56175, -55577, -54963, - -54331, -53683, -53019, -52339, -51643, -50931, -50203, -49460, - -48702, -47929, -47142, -46340, -45525, -44695, -43852, -42995, - -42125, -41243, -40347, -39440, -38521, -37589, -36647, -35693, - -34728, -33753, -32768, -31772, -30767, -29752, -28729, -27696, - -26655, -25606, -24550, -23486, -22414, -21336, -20251, -19160, - -18064, -16961, -15854, -14742, -13625, -12504, -11380, -10252, - -9120, -7986, -6850, -5711, -4571, -3429, -2287, -1143 - }; - - fixed ffsin(int degrees) { - if (degrees < 0) - degrees -= (degrees/360-1)*360; - else if (degrees >= 360) - degrees %= 360; - return sintablefixed[degrees]; - } - - fixed ffcos(int degrees) { - return ffsin(degrees+90); - } - -#endif - -#if GMISC_NEED_INVSQRT - // Algorithm based on Quake code. - #if GMISC_INVSQRT_REAL_SLOW - #include - float invsqrt(float n) { - return 1.0/sqrt(n); - } - #else - float invsqrt(float n) { - int32_t i; - float x2; - - x2 = n * 0.5F; - - // Convert into an int32 (no binary format conversion) - #if GMISC_INVSQRT_MIXED_ENDIAN - ((char *)&i)[0] = ((char *)&n)[3]; - ((char *)&i)[1] = ((char *)&n)[2]; - ((char *)&i)[2] = ((char *)&n)[1]; - ((char *)&i)[3] = ((char *)&n)[0]; - #else - i = *(int32_t *)&n; - #endif - - // evil floating point bit level hacking - i = 0x5F375A86 - (i >> 1); // The quake code used 0x5F3759DF but this is better. - - // Convert back to a float (no binary format conversion) - #if GMISC_INVSQRT_MIXED_ENDIAN - ((char *)&n)[0] = ((char *)&i)[3]; - ((char *)&n)[1] = ((char *)&i)[2]; - ((char *)&n)[2] = ((char *)&i)[1]; - ((char *)&n)[3] = ((char *)&i)[0]; - #else - n = *(float *)&i; - #endif - - n = n * (1.5F - (x2 * n * n)); // 1st iteration - //n = n * (1.5F - (x2 * n * n)); // 2nd iteration for extra precision, this can be removed - return n; - } - #endif -#endif - -#endif /* GFX_USE_GMISC */ -/** @} */ diff --git a/src/gqueue/gqueue.c b/src/gqueue/gqueue.c deleted file mode 100644 index 45ca6fce..00000000 --- a/src/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.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_gqueue.c b/src/gqueue/gqueue_gqueue.c new file mode 100644 index 00000000..5a50c64b --- /dev/null +++ b/src/gqueue/gqueue_gqueue.c @@ -0,0 +1,469 @@ +/* + * 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/sys_make.mk b/src/gqueue/sys_make.mk index ab8a0423..f8a542c3 100644 --- a/src/gqueue/sys_make.mk +++ b/src/gqueue/sys_make.mk @@ -1 +1 @@ -GFXSRC += $(GFXLIB)/src/gqueue/gqueue.c +GFXSRC += $(GFXLIB)/src/gqueue/gqueue_gqueue.c diff --git a/src/gtimer/gtimer.c b/src/gtimer/gtimer.c deleted file mode 100644 index 3e0b6966..00000000 --- a/src/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.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->prev) - 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_gtimer.c b/src/gtimer/gtimer_gtimer.c new file mode 100644 index 00000000..5289b7b8 --- /dev/null +++ b/src/gtimer/gtimer_gtimer.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 + */ + +/** + * @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->prev) + 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/sys_make.mk b/src/gtimer/sys_make.mk index 801c31a6..e48dc9f5 100644 --- a/src/gtimer/sys_make.mk +++ b/src/gtimer/sys_make.mk @@ -1 +1 @@ -GFXSRC += $(GFXLIB)/src/gtimer/gtimer.c +GFXSRC += $(GFXLIB)/src/gtimer/gtimer_gtimer.c diff --git a/src/gwin/button.c b/src/gwin/button.c deleted file mode 100644 index 5507a07a..00000000 --- a/src/gwin/button.c +++ /dev/null @@ -1,328 +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/button.c - * @brief GWIN sub-system button code - */ - -#include "gfx.h" - -#if GFX_USE_GWIN && GWIN_NEED_BUTTON - -#include "src/gwin/class_gwin.h" - -// Parameters for various shapes -#define RND_CNR_SIZE 5 // Rounded corner size for rounded buttons -#define ARROWHEAD_DIVIDER 4 // A quarter of the height for the arrow head -#define ARROWBODY_DIVIDER 4 // A quarter of the width for the arrow body -#define TOP_FADE 50 // (TOP_FADE/255)% fade to white for top of button -#define BOTTOM_FADE 25 // (BOTTOM_FADE/255)% fade to black for bottom of button - -// Our pressed state -#define GBUTTON_FLG_PRESSED (GWIN_FIRST_CONTROL_FLAG<<0) - -#if GINPUT_NEED_MOUSE - // A mouse down has occurred over the button - static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { - (void) x; (void) y; - gw->g.flags |= GBUTTON_FLG_PRESSED; - _gwinUpdate((GHandle)gw); - } - - // A mouse up has occurred (it may or may not be over the button) - static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) { - (void) x; (void) y; - gw->g.flags &= ~GBUTTON_FLG_PRESSED; - _gwinUpdate((GHandle)gw); - - #if !GWIN_BUTTON_LAZY_RELEASE - // If the mouse up was not over the button then cancel the event - if (x < 0 || y < 0 || x >= gw->g.width || y >= gw->g.height) - return; - #endif - - _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON); - } -#endif - -#if GINPUT_NEED_TOGGLE - // A toggle off has occurred - static void ToggleOff(GWidgetObject *gw, uint16_t role) { - (void) role; - gw->g.flags &= ~GBUTTON_FLG_PRESSED; - _gwinUpdate((GHandle)gw); - } - - // A toggle on has occurred - static void ToggleOn(GWidgetObject *gw, uint16_t role) { - (void) role; - gw->g.flags |= GBUTTON_FLG_PRESSED; - _gwinUpdate((GHandle)gw); - // Trigger the event on button down (different than for mouse/touch) - _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON); - } - - static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { - (void) role; - ((GButtonObject *)gw)->toggle = instance; - } - - static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { - (void) role; - return ((GButtonObject *)gw)->toggle; - } -#endif - -// The button VMT table -static const gwidgetVMT buttonVMT = { - { - "Button", // The classname - sizeof(GButtonObject), // The object size - _gwidgetDestroy, // The destroy routine - _gwidgetRedraw, // The redraw routine - 0, // The after-clear routine - }, - gwinButtonDraw_Normal, // The default drawing routine - #if GINPUT_NEED_MOUSE - { - MouseDown, // Process mouse down events - MouseUp, // Process mouse up events - 0, // Process mouse move events (NOT USED) - }, - #endif - #if GINPUT_NEED_TOGGLE - { - 1, // 1 toggle role - ToggleAssign, // Assign Toggles - ToggleGet, // Get Toggles - ToggleOff, // Process toggle off events - ToggleOn, // Process toggle on events - }, - #endif - #if GINPUT_NEED_DIAL - { - 0, // No dial roles - 0, // Assign Dials (NOT USED) - 0, // Get Dials (NOT USED) - 0, // Process dial move events (NOT USED) - }, - #endif -}; - -GHandle gwinGButtonCreate(GDisplay *g, GButtonObject *gw, const GWidgetInit *pInit) { - if (!(gw = (GButtonObject *)_gwidgetCreate(g, &gw->w, pInit, &buttonVMT))) - return 0; - - #if GINPUT_NEED_TOGGLE - gw->toggle = GWIDGET_NO_INSTANCE; - #endif - gwinSetVisible((GHandle)gw, pInit->g.show); - return (GHandle)gw; -} - -bool_t gwinButtonIsPressed(GHandle gh) { - if (gh->vmt != (gwinVMT *)&buttonVMT) - return FALSE; - - return (gh->flags & GBUTTON_FLG_PRESSED) ? TRUE : FALSE; -} - -/*---------------------------------------------------------- - * Custom Draw Routines - *----------------------------------------------------------*/ - -static const GColorSet *getDrawColors(GWidgetObject *gw) { - if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled; - if ((gw->g.flags & GBUTTON_FLG_PRESSED)) return &gw->pstyle->pressed; - return &gw->pstyle->enabled; -} - -#if GWIN_FLAT_STYLING - void gwinButtonDraw_Normal(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; - pcol = getDrawColors(gw); - - gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); - gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); - } -#else - void gwinButtonDraw_Normal(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - fixed alpha; - fixed dalpha; - coord_t i; - color_t tcol, bcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; - pcol = getDrawColors(gw); - - /* Fill the box blended from variants of the fill color */ - tcol = gdispBlendColor(White, pcol->fill, TOP_FADE); - bcol = gdispBlendColor(Black, pcol->fill, BOTTOM_FADE); - dalpha = FIXED(255)/gw->g.height; - for(alpha = 0, i = 0; i < gw->g.height; i++, alpha += dalpha) - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+i, gw->g.x+gw->g.width-2, gw->g.y+i, gdispBlendColor(bcol, tcol, NONFIXED(alpha))); - - gdispGDrawStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, justifyCenter); - gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); - } -#endif - -#if GDISP_NEED_ARC - void gwinButtonDraw_Rounded(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; - pcol = getDrawColors(gw); - - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); - if (gw->g.width >= 2*RND_CNR_SIZE+10) { - gdispGFillRoundedBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, RND_CNR_SIZE-1, pcol->fill); - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+RND_CNR_SIZE, gw->g.width-2, gw->g.height-(2*RND_CNR_SIZE), gw->text, gw->g.font, pcol->text, justifyCenter); - gdispGDrawRoundedBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, RND_CNR_SIZE, pcol->edge); - } else { - gdispGFillStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); - } - } -#endif - -#if GDISP_NEED_ELLIPSE - void gwinButtonDraw_Ellipse(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; - pcol = getDrawColors(gw); - - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); - gdispGFillEllipse(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width/2-1, gw->g.height/2-1, pcol->fill); - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - gdispGDrawEllipse(gw->g.display, gw->g.x, gw->g.y, gw->g.width/2, gw->g.height/2, pcol->edge); - } -#endif - -#if GDISP_NEED_CONVEX_POLYGON - void gwinButtonDraw_ArrowUp(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - (void) param; - point arw[7]; - - if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; - pcol = getDrawColors(gw); - - arw[0].x = gw->g.width/2; arw[0].y = 0; - arw[1].x = gw->g.width-1; arw[1].y = gw->g.height/ARROWHEAD_DIVIDER; - arw[2].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[2].y = gw->g.height/ARROWHEAD_DIVIDER; - arw[3].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[3].y = gw->g.height-1; - arw[4].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[4].y = gw->g.height-1; - arw[5].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[5].y = gw->g.height/ARROWHEAD_DIVIDER; - arw[6].x = 0; arw[6].y = gw->g.height/ARROWHEAD_DIVIDER; - - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); - gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); - gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - } - - void gwinButtonDraw_ArrowDown(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - (void) param; - point arw[7]; - - if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; - pcol = getDrawColors(gw); - - arw[0].x = gw->g.width/2; arw[0].y = gw->g.height-1; - arw[1].x = gw->g.width-1; arw[1].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; - arw[2].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[2].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; - arw[3].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[3].y = 0; - arw[4].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[4].y = 0; - arw[5].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[5].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; - arw[6].x = 0; arw[6].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; - - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); - gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); - gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - } - - void gwinButtonDraw_ArrowLeft(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - (void) param; - point arw[7]; - - if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; - pcol = getDrawColors(gw); - - arw[0].x = 0; arw[0].y = gw->g.height/2; - arw[1].x = gw->g.width/ARROWHEAD_DIVIDER; arw[1].y = 0; - arw[2].x = gw->g.width/ARROWHEAD_DIVIDER; arw[2].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; - arw[3].x = gw->g.width-1; arw[3].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; - arw[4].x = gw->g.width-1; arw[4].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; - arw[5].x = gw->g.width/ARROWHEAD_DIVIDER; arw[5].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; - arw[6].x = gw->g.width/ARROWHEAD_DIVIDER; arw[6].y = gw->g.height-1; - - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); - gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); - gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - } - - void gwinButtonDraw_ArrowRight(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - (void) param; - point arw[7]; - - if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; - pcol = getDrawColors(gw); - - arw[0].x = gw->g.width-1; arw[0].y = gw->g.height/2; - arw[1].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[1].y = 0; - arw[2].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[2].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; - arw[3].x = 0; arw[3].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; - arw[4].x = 0; arw[4].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; - arw[5].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[5].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; - arw[6].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[6].y = gw->g.height-1; - - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); - gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); - gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - } -#endif - -#if GDISP_NEED_IMAGE || defined(__DOXYGEN__) - void gwinButtonDraw_Image(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - coord_t sy; - - if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; - pcol = getDrawColors(gw); - - if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) { - sy = 2 * gw->g.height; - } else if ((gw->g.flags & GBUTTON_FLG_PRESSED)) { - sy = gw->g.height; - } else { - sy = 0; - } - - gdispGImageDraw(gw->g.display, (gdispImage *)param, gw->g.x, gw->g.y, gw->g.width, gw->g.height, 0, sy); - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - } -#endif - -#endif /* GFX_USE_GWIN && GWIN_NEED_BUTTON */ diff --git a/src/gwin/button.h b/src/gwin/button.h deleted file mode 100644 index 5f5d2838..00000000 --- a/src/gwin/button.h +++ /dev/null @@ -1,135 +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/button.h - * @brief GWIN Graphic window subsystem header file. - * - * @defgroup Button Button - * @ingroup Widgets - * - * @details GWIN allows it to easily create buttons with different styles - * and check for different meta states such as: PRESSED, CLICKED, - * RELEASED etc. - * - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_BUTTON must be set to TRUE in your gfxconf.h - * @{ - */ - -#ifndef _GWIN_BUTTON_H -#define _GWIN_BUTTON_H - -/* This file is included within "gwin/gwidget.h" */ - -/** - * @brief The Event Type for a Button Event - */ -#define GEVENT_GWIN_BUTTON (GEVENT_GWIN_CTRL_FIRST+0) - -/** - * @brief A Button Event - * @note There are currently no GEventGWinButton listening flags - use 0 as the flags to @p gwinAttachListener() - */ -typedef GEventGWin GEventGWinButton; - -/** - * @brief The button widget structure - * @note Do not use the members directly - treat it as a black-box. - */ -typedef struct GButtonObject { - GWidgetObject w; - #if GINPUT_NEED_TOGGLE - uint16_t toggle; - #endif -} GButtonObject; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create a button widget. - * @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] gb The GButtonObject structure to initialise. If this is NULL the structure is dynamically allocated. - * @param[in] pInit The initialisation parameters - * - * @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 button remembers its normal drawing state. If there is a window manager then it is automatically - * redrawn if the window is moved or its visibility state is changed. - * @note A button supports mouse and a toggle input. - * @note When assigning a toggle, only one toggle is supported. If you try to assign more than one toggle it will - * forget the previous toggle. When assigning a toggle the role parameter must be 0. - * - * @api - */ -GHandle gwinGButtonCreate(GDisplay *g, GButtonObject *gb, const GWidgetInit *pInit); -#define gwinButtonCreate(gb, pInit) gwinGButtonCreate(GDISP, gb, pInit) - -/** - * @brief Is the button current pressed - * @return TRUE if the button is pressed - * - * @param[in] gh The window handle (must be a button widget) - * - * @api - */ -bool_t gwinButtonIsPressed(GHandle gh); - -/** - * @brief Some custom button drawing routines - * @details These function may be passed to @p gwinSetCustomDraw() to get different button drawing styles - * - * @param[in] gw The widget object (in this case a button) - * @param[in] param A parameter passed in from the user - * - * @note In your custom button drawing function you may optionally call these - * standard functions and then draw your extra details on top. - * @note The standard functions below ignore the param parameter except for @p gwinButtonDraw_Image(). - * @note The image custom draw function @p gwinButtonDraw_Image() uses param to pass in the gdispImage pointer. - * The image must be already opened before calling @p gwinSetCustomDraw(). The image should be 3 - * times the height of the button. The button image is repeated 3 times vertically, the first (top) for - * the "up" image, the 2nd for the "down" image, and the third (bottom) image for the disabled state. If - * the disabled state is never going to be used then the image can be just 2 times the button height. - * No checking is done to compare the size of the button to the size of the image. - * Note text is drawn on top of the image. - * @note These custom drawing routines don't have to worry about setting clipping as the framework - * sets clipping to the object window prior to calling these routines. - * - * @api - * @{ - */ -void gwinButtonDraw_Normal(GWidgetObject *gw, void *param); // @< A standard button -#if GDISP_NEED_ARC || defined(__DOXYGEN__) - void gwinButtonDraw_Rounded(GWidgetObject *gw, void *param); // @< A rounded rectangle button -#endif -#if GDISP_NEED_ELLIPSE || defined(__DOXYGEN__) - void gwinButtonDraw_Ellipse(GWidgetObject *gw, void *param); // @< A circular button -#endif -#if GDISP_NEED_CONVEX_POLYGON || defined(__DOXYGEN__) - void gwinButtonDraw_ArrowUp(GWidgetObject *gw, void *param); // @< An up arrow button - void gwinButtonDraw_ArrowDown(GWidgetObject *gw, void *param); // @< A down arrow button - void gwinButtonDraw_ArrowLeft(GWidgetObject *gw, void *param); // @< A left arrow button - void gwinButtonDraw_ArrowRight(GWidgetObject *gw, void *param); // @< A right arrow button -#endif -#if GDISP_NEED_IMAGE || defined(__DOXYGEN__) - void gwinButtonDraw_Image(GWidgetObject *gw, void *param); // @< An image button - see the notes above on the param. -#endif -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif /* _GWIN_BUTTON_H */ -/** @} */ - diff --git a/src/gwin/checkbox.c b/src/gwin/checkbox.c deleted file mode 100644 index 6269acc1..00000000 --- a/src/gwin/checkbox.c +++ /dev/null @@ -1,195 +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/checkbox.c - * @brief GWIN sub-system button code - */ - -#include "gfx.h" - -#if (GFX_USE_GWIN && GWIN_NEED_CHECKBOX) || defined(__DOXYGEN__) - -#include "src/gwin/class_gwin.h" - -// Our checked state -#define GCHECKBOX_FLG_CHECKED (GWIN_FIRST_CONTROL_FLAG<<0) - -// Send the checkbox event -static void SendCheckboxEvent(GWidgetObject *gw) { - GSourceListener * psl; - GEvent * pe; - #define pce ((GEventGWinCheckbox *)pe) - - // Trigger a GWIN Checkbox Event - psl = 0; - while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { - if (!(pe = geventGetEventBuffer(psl))) - continue; - pce->type = GEVENT_GWIN_CHECKBOX; - pce->gwin = &gw->g; - pce->isChecked = (gw->g.flags & GCHECKBOX_FLG_CHECKED) ? TRUE : FALSE; - #if GWIN_WIDGET_TAGS - pce->tag = gw->tag; - #endif - geventSendEvent(psl); - } - - #undef pce -} - -#if GINPUT_NEED_MOUSE - static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { - (void) x; (void) y; - gw->g.flags ^= GCHECKBOX_FLG_CHECKED; - _gwinUpdate((GHandle)gw); - SendCheckboxEvent(gw); - } -#endif - -#if GINPUT_NEED_TOGGLE - static void ToggleOn(GWidgetObject *gw, uint16_t role) { - (void) role; - gw->g.flags ^= GCHECKBOX_FLG_CHECKED; - _gwinUpdate((GHandle)gw); - SendCheckboxEvent(gw); - } - - static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { - (void) role; - ((GCheckboxObject *)gw)->toggle = instance; - } - - static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { - (void) role; - return ((GCheckboxObject *)gw)->toggle; - } -#endif - -// The checkbox VMT table -static const gwidgetVMT checkboxVMT = { - { - "Checkbox", // The classname - sizeof(GCheckboxObject),// The object size - _gwidgetDestroy, // The destroy routine - _gwidgetRedraw, // The redraw routine - 0, // The after-clear routine - }, - gwinCheckboxDraw_CheckOnLeft, // The default drawing routine - #if GINPUT_NEED_MOUSE - { - MouseDown, // Process mouse down events - 0, // Process mouse up events (NOT USED) - 0, // Process mouse move events (NOT USED) - }, - #endif - #if GINPUT_NEED_TOGGLE - { - 1, // 1 toggle role - ToggleAssign, // Assign Toggles - ToggleGet, // Get Toggles - 0, // Process toggle off events (NOT USED) - ToggleOn, // Process toggle on events - }, - #endif - #if GINPUT_NEED_DIAL - { - 0, // No dial roles - 0, // Assign Dials (NOT USED) - 0, // Get Dials (NOT USED) - 0, // Process dial move events (NOT USED) - }, - #endif -}; - -GHandle gwinGCheckboxCreate(GDisplay *g, GCheckboxObject *gb, const GWidgetInit *pInit) { - if (!(gb = (GCheckboxObject *)_gwidgetCreate(g, &gb->w, pInit, &checkboxVMT))) - return 0; - - #if GINPUT_NEED_TOGGLE - gb->toggle = GWIDGET_NO_INSTANCE; - #endif - gwinSetVisible((GHandle)gb, pInit->g.show); - return (GHandle)gb; -} - -void gwinCheckboxCheck(GHandle gh, bool_t isChecked) { - if (gh->vmt != (gwinVMT *)&checkboxVMT) - return; - - if (isChecked) { - if ((gh->flags & GCHECKBOX_FLG_CHECKED)) return; - gh->flags |= GCHECKBOX_FLG_CHECKED; - } else { - if (!(gh->flags & GCHECKBOX_FLG_CHECKED)) return; - gh->flags &= ~GCHECKBOX_FLG_CHECKED; - } - _gwinUpdate(gh); - SendCheckboxEvent((GWidgetObject *)gh); -} - -bool_t gwinCheckboxIsChecked(GHandle gh) { - if (gh->vmt != (gwinVMT *)&checkboxVMT) - return FALSE; - - return (gh->flags & GCHECKBOX_FLG_CHECKED) ? TRUE : FALSE; -} - -/*---------------------------------------------------------- - * Custom Draw Routines - *----------------------------------------------------------*/ - -static const GColorSet *getDrawColors(GWidgetObject *gw) { - if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled; - if ((gw->g.flags & GCHECKBOX_FLG_CHECKED)) return &gw->pstyle->pressed; - return &gw->pstyle->enabled; -} - -void gwinCheckboxDraw_CheckOnLeft(GWidgetObject *gw, void *param) { - #define gcw ((GCheckboxObject *)gw) - coord_t ld, df; - const GColorSet * pcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&checkboxVMT) return; - pcol = getDrawColors(gw); - - ld = gw->g.width < gw->g.height ? gw->g.width : gw->g.height; - gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+1, ld, ld-2, gw->pstyle->background); - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, ld, ld, pcol->edge); - - df = ld < 4 ? 1 : 2; - if (gw->g.flags & GCHECKBOX_FLG_CHECKED) - gdispGFillArea(gw->g.display, gw->g.x+df, gw->g.y+df, ld-2*df, ld-2*df, pcol->fill); - - gdispGFillStringBox(gw->g.display, gw->g.x+ld+1, gw->g.y, gw->g.width-ld-1, gw->g.height, gw->text, gw->g.font, pcol->text, gw->pstyle->background, justifyLeft); - #undef gcw -} - -void gwinCheckboxDraw_CheckOnRight(GWidgetObject *gw, void *param) { - #define gcw ((GCheckboxObject *)gw) - coord_t ep, ld, df; - const GColorSet * pcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&checkboxVMT) return; - pcol = getDrawColors(gw); - - ld = gw->g.width < gw->g.height ? gw->g.width : gw->g.height; - ep = gw->g.width-ld-1; - gdispGFillArea(gw->g.display, gw->g.x+ep-1, gw->g.y+1, ld, ld-2, gw->pstyle->background); - gdispGDrawBox(gw->g.display, gw->g.x+ep, gw->g.y, ld, ld, pcol->edge); - - df = ld < 4 ? 1 : 2; - if (gw->g.flags & GCHECKBOX_FLG_CHECKED) - gdispGFillArea(gw->g.display, gw->g.x+ep+df, gw->g.y+df, ld-2*df, ld-2*df, pcol->fill); - - gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, ep-1, gw->g.height, gw->text, gw->g.font, pcol->text, gw->pstyle->background, justifyRight); - #undef gcw -} - -#endif /* (GFX_USE_GWIN && GWIN_NEED_CHECKBOX) */ diff --git a/src/gwin/checkbox.h b/src/gwin/checkbox.h deleted file mode 100644 index 3c426242..00000000 --- a/src/gwin/checkbox.h +++ /dev/null @@ -1,127 +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/checkbox.h - * @brief GWIN Graphic window subsystem header file. - * - * @defgroup Checkbox Checkbox - * @ingroup Widgets - * - * @details GWIN allows it to easily create a group of checkbox buttons. - * - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_CHECKBOX must be set to TRUE in your gfxconf.h - * @{ - */ - -#ifndef _GWIN_CHECKBOX_H -#define _GWIN_CHECKBOX_H - -/* This file is included within "gwin/gwidget.h" */ - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -#define GEVENT_GWIN_CHECKBOX (GEVENT_GWIN_CTRL_FIRST+2) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -typedef struct GEventGWinCheckbox { - GEventType type; // The type of this event (GEVENT_GWIN_CHECKBOX) - GHandle gwin; // The checkbox that has been depressed (actually triggered on release) - #if GWIN_WIDGET_TAGS - WidgetTag tag; // The checkbox tag - #endif - bool_t isChecked; // Is the checkbox currently checked or unchecked? -} GEventGWinCheckbox; - -/* A Checkbox window */ -typedef struct GCheckboxObject { - GWidgetObject w; - #if GINPUT_NEED_TOGGLE - uint16_t toggle; - #endif -} GCheckboxObject; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create a checkbox 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] gb The GCheckboxObject structure to initialise. If this is NULL, the structure is dynamically allocated. - * @param[in] pInit The initialization parameters to use - * - * @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 checkbox remembers its normal drawing state. If there is a window manager then it is automatically - * redrawn if the window is moved or its visibility state is changed. - * @note A checkbox supports mouse and a toggle input. - * @note When assigning a toggle, only one toggle is supported. If you try to assign more than one toggle it will - * forget the previous toggle. When assigning a toggle the role parameter must be 0. - * - * @api - */ -GHandle gwinGCheckboxCreate(GDisplay *g, GCheckboxObject *gb, const GWidgetInit *pInit); -#define gwinCheckboxCreate(gb, pInit) gwinGCheckboxCreate(GDISP, gb, pInit) - -/** - * @brief Set the state of a checkbox - * - * @param[in] gh The window handle (must be a checkbox window) - * @param[in] isChecked TRUE to set the check, FALSE to uncheck. - * - * @api - */ -void gwinCheckboxCheck(GHandle gh, bool_t isChecked); - -/** - * @brief Get the state of a checkbox - * @return TRUE if the checkbox is currently checked - * - * @param[in] gh The window handle (must be a checkbox window) - * - * @api - */ -bool_t gwinCheckboxIsChecked(GHandle gh); - -/** - * @brief Some custom checkbox drawing routines - * @details These function may be passed to @p gwinSetCustomDraw() to get different checkbox drawing styles - * - * @param[in] gw The widget (which must be a checkbox) - * @param[in] param A parameter passed in from the user - * - * @note In your custom checkbox drawing function you may optionally call this - * standard functions and then draw your extra details on top. - * @note The standard functions below ignore the param parameter. - * @note These custom drawing routines don't have to worry about setting clipping as the framework - * sets clipping to the object window prior to calling these routines. - * - * @api - * @{ - */ -void gwinCheckboxDraw_CheckOnLeft(GWidgetObject *gw, void *param); -void gwinCheckboxDraw_CheckOnRight(GWidgetObject *gw, void *param); -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif /* _GWIN_CHECKBOX_H */ -/** @} */ - diff --git a/src/gwin/class_gwin.h b/src/gwin/class_gwin.h deleted file mode 100644 index e45914a6..00000000 --- a/src/gwin/class_gwin.h +++ /dev/null @@ -1,363 +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/class_gwin.h - * @brief GWIN Graphic window subsystem header file. - * - * @defgroup Internal Internal - * @ingroup GWIN - * - * @note These definitions are normally not used by an application program. They are useful - * only if you want to create your own custom GWIN window or widget. - * @note To access these definitions you must include "gwin/class_gwin.h" in your source file. - * - * @{ - */ -#ifndef _CLASS_GWIN_H -#define _CLASS_GWIN_H - -#if GFX_USE_GWIN || defined(__DOXYGEN__) - -#if defined(__KEIL__) || defined(__C51__) - #pragma anon_unions -#endif - -/** - * @brief The predefined flags for a Window - * @{ - */ -#define GWIN_FIRST_CONTROL_FLAG 0x00000001 // @< 8 bits free for the control to use -#define GWIN_LAST_CONTROL_FLAG 0x00000080 // @< 8 bits free for the control to use -#define GWIN_FLG_VISIBLE 0x00000100 // @< The window is "visible" -#define GWIN_FLG_SYSVISIBLE 0x00000200 // @< The window is visible after parents are tested -#define GWIN_FLG_ENABLED 0x00000400 // @< The window is "enabled" -#define GWIN_FLG_SYSENABLED 0x00000800 // @< The window is enabled after parents are tested -#define GWIN_FLG_DYNAMIC 0x00001000 // @< The GWIN structure is allocated -#define GWIN_FLG_ALLOCTXT 0x00002000 // @< The text/label is allocated -#define GWIN_FLG_NEEDREDRAW 0x00004000 // @< Redraw is needed but has been delayed -#define GWIN_FLG_BGREDRAW 0x00008000 // @< On redraw, if not visible redraw the revealed under-side -#define GWIN_FLG_SUPERMASK 0x000F0000 // @< The bit mask to leave just the window superclass type -#define GWIN_FLG_WIDGET 0x00010000 // @< This is a widget -#define GWIN_FLG_CONTAINER 0x00020000 // @< This is a container -#define GWIN_FLG_MINIMIZED 0x00100000 // @< The window is minimized -#define GWIN_FLG_MAXIMIZED 0x00200000 // @< The window is maximized -#define GWIN_FLG_MOUSECAPTURE 0x00400000 // @< The window has captured the mouse -#define GWIN_FIRST_WM_FLAG 0x01000000 // @< 8 bits free for the window manager to use -#define GWIN_LAST_WM_FLAG 0x80000000 // @< 8 bits free for the window manager to use -/** @} */ - -/** - * @brief The Virtual Method Table for a GWIN window - * @{ - */ -typedef struct gwinVMT { - const char * classname; // @< The GWIN classname (mandatory) - size_t size; // @< The size of the class object - void (*Destroy) (GWindowObject *gh); // @< The GWIN destroy function (optional) - void (*Redraw) (GWindowObject *gh); // @< The GWIN redraw routine (optional) - void (*AfterClear) (GWindowObject *gh); // @< The GWIN after-clear function (optional) -} gwinVMT; -/** @} */ - -#if GWIN_NEED_WIDGET || defined(__DOXYGEN__) - - /** - * @brief An toggle/dial instance is not being used - */ - #define GWIDGET_NO_INSTANCE ((uint16_t)-1) - - /** - * @brief The source handle that widgets use when sending events - */ - #define GWIDGET_SOURCE ((GSourceHandle)(void *)_gwidgetCreate) - - /** - * @brief The Virtual Method Table for a widget - * @note A widget must have a destroy function. Either use @p _gwidgetDestroy() or use your own function - * which internally calls @p _gwidgetDestroy(). - * @note A widget must have a redraw function. Use @p _gwidgetRedraw(). - * @note If toggleroles != 0, ToggleAssign(), ToggleGet() and one or both of ToggleOff() and ToggleOn() must be specified. - * @note If dialroles != 0, DialAssign(), DialGet() and DialMove() must be specified. - * @{ - */ - typedef struct gwidgetVMT { - struct gwinVMT g; // @< This is still a GWIN - void (*DefaultDraw) (GWidgetObject *gw, void *param); // @< The default drawing routine (mandatory) - #if GINPUT_NEED_MOUSE - struct { - void (*MouseDown) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse down events (optional) - void (*MouseUp) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse up events (optional) - void (*MouseMove) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse move events (optional) - }; - #endif - #if GINPUT_NEED_TOGGLE - struct { - uint16_t toggleroles; // @< The roles supported for toggles (0->toggleroles-1) - void (*ToggleAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); // @< Assign a toggle to a role (optional) - uint16_t (*ToggleGet) (GWidgetObject *gw, uint16_t role); // @< Return the instance for a particular role (optional) - void (*ToggleOff) (GWidgetObject *gw, uint16_t role); // @< Process toggle off events (optional) - void (*ToggleOn) (GWidgetObject *gw, uint16_t role); // @< Process toggle on events (optional) - }; - #endif - #if GINPUT_NEED_DIAL - struct { - uint16_t dialroles; // @< The roles supported for dials (0->dialroles-1) - void (*DialAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); // @< Test the role and save the dial instance handle (optional) - uint16_t (*DialGet) (GWidgetObject *gw, uint16_t role); // @< Return the instance for a particular role (optional) - void (*DialMove) (GWidgetObject *gw, uint16_t role, uint16_t value, uint16_t max); // @< Process dial move events (optional) - }; - #endif - } gwidgetVMT; - /** @} */ -#endif - -#if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) - - /** - * @brief The Virtual Method Table for a container - * @note A container must have a destroy function. Either use @p _gcontainerDestroy() or use your own function - * which internally calls @p _gcontainerDestroy(). - * @note A container must have a gwin redraw function. Use @p _containerRedraw(). - * @note If toggleroles != 0, ToggleAssign(), ToggleGet() and one or both of ToggleOff() and ToggleOn() must be specified. - * @note If dialroles != 0, DialAssign(), DialGet() and DialMove() must be specified. - * @{ - */ - typedef struct gcontainerVMT { - gwidgetVMT gw; - coord_t (*LeftBorder) (GHandle gh); // @< The size of the left border (mandatory) - coord_t (*TopBorder) (GHandle gh); // @< The size of the top border (mandatory) - coord_t (*RightBorder) (GHandle gh); // @< The size of the right border (mandatory) - coord_t (*BottomBorder) (GHandle gh); // @< The size of the bottom border (mandatory) - void (*NotifyAdd) (GHandle gh, GHandle ghChild); // @< Notification that a child has been added (optional) - void (*NotifyDelete) (GHandle gh, GHandle ghChild); // @< Notification that a child has been deleted (optional) - } gcontainerVMT; - /** @} */ -#endif - -#if GWIN_NEED_WINDOWMANAGER || defined(__DOXYGEN__) - // @note There is only ever one instance of each GWindowManager type - typedef struct GWindowManager { - const struct gwmVMT *vmt; - } GWindowManager; - - /** - * @brief The Virtual Method Table for a window manager - * @{ - */ - typedef struct gwmVMT { - void (*Init) (void); // @< The window manager has just been set as the current window manager - void (*DeInit) (void); // @< The window manager has just been removed as the current window manager - bool_t (*Add) (GHandle gh, const GWindowInit *pInit); // @< A window has been added - void (*Delete) (GHandle gh); // @< A window has been deleted - void (*Redraw) (GHandle gh); // @< A window needs to be redraw (or undrawn) - void (*Size) (GHandle gh, coord_t w, coord_t h); // @< A window wants to be resized - void (*Move) (GHandle gh, coord_t x, coord_t y); // @< A window wants to be moved - void (*Raise) (GHandle gh); // @< A window wants to be on top - void (*MinMax) (GHandle gh, GWindowMinMax minmax); // @< A window wants to be minimized/maximised - } gwmVMT; - /** @} */ - - /** - * @brief The current window manager - */ - extern GWindowManager * _GWINwm; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Initialise (and allocate if necessary) the base GWIN object - * - * @param[in] g The GDisplay to use for this window - * @param[in] pgw The GWindowObject structure. If NULL one is allocated from the heap - * @param[in] pInit The user initialization parameters - * @param[in] vmt The virtual method table for the GWIN object - * @param[in] flags The default flags to use - * - * @return The GHandle of the created window - * - * @notapi - */ -GHandle _gwindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit, const gwinVMT *vmt, uint32_t flags); - -/** - * @brief Redraw the window after a status change. - * - * @param[in] gh The widget to redraw - * - * @note Mark a window for redraw. - * @note The window will get redrawn at some later time. - * @note This call is designed to be fast and non-blocking - * - * @notapi - */ -void _gwinUpdate(GHandle gh); - -/** - * @brief How to flush the redraws - * @notes REDRAW_WAIT - Wait for a drawing session to be available - * @notes REDRAW_NOWAIT - Do nothing if the drawing session is not available - * @note REDRAW_INSESSION - We are already in a drawing session - */ -typedef enum GRedrawMethod { REDRAW_WAIT, REDRAW_NOWAIT, REDRAW_INSESSION } GRedrawMethod; - -/** - * @brief Flush any pending redraws in the system. - * - * @param[in] how Do we wait for the lock? - * - * @note This call will attempt to flush any pending redraws - * in the system. The doWait parameter tells this call - * how to handle someone already holding the drawing lock. - * If doWait is TRUE it waits to obtain the lock. If FALSE - * and the drawing lock is free then the redraw is done - * immediately. If the drawing lock was taken it will postpone the flush - * on the basis that someone else will do it for us later. - * - * @notapi - */ -void _gwinFlushRedraws(GRedrawMethod how); - -/** - * @brief Obtain a drawing session - * @return TRUE if the drawing session was obtained, FALSE if the window is not visible - * - * @param[in] gh The window - * - * @note This function blocks until a drawing session is available if the window is visible - */ -bool_t _gwinDrawStart(GHandle gh); - -/** - * @brief Release a drawing session - * - * @param[in] gh The window - */ -void _gwinDrawEnd(GHandle gh); - -/** - * @brief Destroy a window. - * - * @param[in] gh The window - * @param[in] how Do we wait for the lock? - * - * @note This call will delete the window. If called without the - * drawing lock 'how' must be REDRAW_WAIT. If called with the drawing - * lock 'how' must be REDRAW_INSESSION. - * - * @notapi - */ -void _gwinDestroy(GHandle gh, GRedrawMethod how); - -/** - * @brief Add a window to the window manager and set its position and size - * @return TRUE if successful - * - * @param[in] gh The window - * @param[in] pInit The window init structure - */ -bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit); - -#if GWIN_NEED_WIDGET || defined(__DOXYGEN__) - /** - * @brief Initialise (and allocate if necessary) the base Widget object - * - * @param[in] g The GDisplay to display this window on - * @param[in] pgw The GWidgetObject structure. If NULL one is allocated from the heap - * @param[in] pInit The user initialization parameters - * @param[in] vmt The virtual method table for the Widget object - * - * @return The GHandle of the created widget - * - * @notapi - */ - GHandle _gwidgetCreate(GDisplay *g, GWidgetObject *pgw, const GWidgetInit *pInit, const gwidgetVMT *vmt); - - /** - * @brief Destroy the Widget object - * - * @param[in] gh The widget to destroy - * - * @notapi - */ - void _gwidgetDestroy(GHandle gh); - - /** - * @brief Redraw the Widget object (VMT method only) - * - * @param[in] gh The widget to redraw - * - * @note Do not use this routine to update a widget after a status change. - * Use @p _gwinUpdate() instead. This routine should only be used in the - * VMT. - * - * @notapi - */ - void _gwidgetRedraw(GHandle gh); - - /** - * @brief Send a standard GWIN event. - * - * @param[in] gh The window - * @param[in] type The event type - * - * @note No consideration is given to recording EVENT LOST statuses. - * - * @notapi - */ - void _gwinSendEvent(GHandle gh, GEventType type); -#endif - -#if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) - /** - * @brief Initialise (and allocate if necessary) the base Container object - * - * @param[in] g The GDisplay to display this window on - * @param[in] pgw The GContainerObject structure. If NULL one is allocated from the heap - * @param[in] pInit The user initialization parameters - * @param[in] vmt The virtual method table for the Container object - * - * @return The GHandle of the created widget - * - * @notapi - */ - GHandle _gcontainerCreate(GDisplay *g, GContainerObject *pgw, const GWidgetInit *pInit, const gcontainerVMT *vmt); - - /** - * @brief Destroy the Container object - * - * @param[in] gh The container to destroy - * - * @notapi - */ - void _gcontainerDestroy(GHandle gh); - - /** - * @brief Redraw the Container object (VMT method only) - * - * @param[in] gh The container to redraw - * - * @note Do not use this routine to update a container after a status change. - * Use @p _gwinUpdate() instead. This routine should only be used in the - * VMT. - * - * @notapi - */ - #define _gcontainerRedraw _gwidgetRedraw -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GWIN */ - -#endif /* _CLASS_GWIN_H */ -/** @} */ diff --git a/src/gwin/console.c b/src/gwin/console.c deleted file mode 100644 index 0380fd61..00000000 --- a/src/gwin/console.c +++ /dev/null @@ -1,809 +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/console.c - * @brief GWIN sub-system console code. - */ - -#include "gfx.h" - -#if GFX_USE_GWIN && GWIN_NEED_CONSOLE - -#include - -#include "src/gwin/class_gwin.h" - -#define GWIN_CONSOLE_USE_CLEAR_LINES TRUE // Clear each line before using it -#define GWIN_CONSOLE_USE_FILLED_CHARS FALSE // Use filled characters instead of drawn characters -#define GWIN_CONSOLE_BUFFER_SCROLLING TRUE // Use the history buffer to scroll when it is available - -// Our control flags -#define GCONSOLE_FLG_NOSTORE (GWIN_FIRST_CONTROL_FLAG<<0) -#define GCONSOLE_FLG_OVERRUN (GWIN_FIRST_CONTROL_FLAG<<1) - -// Meaning of our attribute bits. -#define ESC_REDBIT 0x01 -#define ESC_GREENBIT 0x02 -#define ESC_BLUEBIT 0x04 -#define ESC_USECOLOR 0x08 -#define ESC_UNDERLINE 0x10 -#define ESC_BOLD 0x20 - -/* - * Stream interface implementation. The interface is write only - */ - -#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM - #define Stream2GWindow(ip) ((GHandle)(((char *)(ip)) - (size_t)(&(((GConsoleObject *)0)->stream)))) - - static size_t GWinStreamWrite(void *ip, const uint8_t *bp, size_t n) { gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; } - static size_t GWinStreamRead(void *ip, uint8_t *bp, size_t n) { (void)ip; (void)bp; (void)n; return 0; } - static msg_t GWinStreamPut(void *ip, uint8_t b) { gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; } - static msg_t GWinStreamGet(void *ip) {(void)ip; return RDY_OK; } - static msg_t GWinStreamPutTimed(void *ip, uint8_t b, systime_t time) { (void)time; gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; } - static msg_t GWinStreamGetTimed(void *ip, systime_t timeout) { (void)ip; (void)timeout; return RDY_OK; } - static size_t GWinStreamWriteTimed(void *ip, const uint8_t *bp, size_t n, systime_t time) { (void)time; gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; } - static size_t GWinStreamReadTimed(void *ip, uint8_t *bp, size_t n, systime_t time) { (void)ip; (void)bp; (void)n; (void)time; return 0; } - - struct GConsoleWindowVMT_t { - _base_asynchronous_channel_methods - }; - - static const struct GConsoleWindowVMT_t GWindowConsoleVMT = { - GWinStreamWrite, - GWinStreamRead, - GWinStreamPut, - GWinStreamGet, - GWinStreamPutTimed, - GWinStreamGetTimed, - GWinStreamWriteTimed, - GWinStreamReadTimed - }; -#endif - -#if GWIN_CONSOLE_ESCSEQ - // Convert escape sequences to attributes - static bool_t ESCtoAttr(char c, uint8_t *pattr) { - uint8_t attr; - - attr = pattr[0]; - switch(c) { - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - attr &= ~(ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT); - attr |= (c - '0') | ESC_USECOLOR; - break; - case 'C': - attr &= ~(ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT|ESC_USECOLOR); - break; - case 'u': - attr |= ESC_UNDERLINE; - break; - case 'U': - attr &= ~ESC_UNDERLINE; - break; - case 'b': - attr |= ESC_BOLD; - break; - case 'B': - attr &= ~ESC_BOLD; - break; - default: - return FALSE; - } - if (attr == pattr[0]) - return FALSE; - pattr[0] = attr; - return TRUE; - } - - static color_t ESCPrintColor(GConsoleObject *gcw) { - switch(gcw->currattr & (ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT|ESC_USECOLOR)) { - case (ESC_USECOLOR): - return Black; - case (ESC_USECOLOR|ESC_REDBIT): - return Red; - case (ESC_USECOLOR|ESC_GREENBIT): - return Green; - case (ESC_USECOLOR|ESC_REDBIT|ESC_GREENBIT): - return Yellow; - case (ESC_USECOLOR|ESC_BLUEBIT): - return Blue; - case (ESC_USECOLOR|ESC_REDBIT|ESC_BLUEBIT): - return Magenta; - case (ESC_USECOLOR|ESC_GREENBIT|ESC_BLUEBIT): - return Cyan; - case (ESC_USECOLOR|ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT): - return White; - default: - return gcw->g.color; - } - } -#else - #define ESCPrintColor(gcw) ((gcw)->g.color) -#endif - -#if GWIN_CONSOLE_USE_HISTORY - static void HistoryDestroy(GWindowObject *gh) { - #define gcw ((GConsoleObject *)gh) - - // Deallocate the history buffer if required. - if (gcw->buffer) { - gfxFree(gcw->buffer); - gcw->buffer = 0; - } - - #undef gcw - } - - static void HistoryRedraw(GWindowObject *gh) { - #define gcw ((GConsoleObject *)gh) - - // No redrawing if there is no history - if (!gcw->buffer) - return; - - // We are printing the buffer - don't store it again - gh->flags |= GCONSOLE_FLG_NOSTORE; - - #if !GWIN_CONSOLE_USE_CLEAR_LINES - // Clear the screen - gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); - #endif - - // Reset the cursor - gcw->cx = 0; - gcw->cy = 0; - - // Reset the current attributes - #if GWIN_CONSOLE_ESCSEQ - gcw->currattr = gcw->startattr; - #endif - - // Print the buffer - gwinPutCharArray(gh, gcw->buffer, gcw->bufpos); - - #if GWIN_CONSOLE_USE_CLEAR_LINES - // Clear the remaining space - { - coord_t y; - - y = gcw->cy; - if (gcw->cx) - y += gdispGetFontMetric(gh->font, fontHeight); - if (y < gh->height) - gdispGFillArea(gh->display, gh->x, gh->y+y, gh->width, gh->height-y, gh->bgcolor); - } - #endif - - // Turn back on storing of buffer contents - gh->flags &= ~GCONSOLE_FLG_NOSTORE; - - #undef gcw - } - - /** - * Put a character into our history buffer - */ - static void putCharInBuffer(GConsoleObject *gcw, char c) { - // Only store if we need to - if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE)) - return; - - // Do we have enough space in the buffer - if (gcw->bufpos >= gcw->bufsize) { - char *p, *ep; - size_t dp; - - /** - * This should never really happen except if the user has changed the window - * size without turning off and then on the buffer. Even then it is unlikely - * because of our conservative allocation strategy. - * If it really is needed we scroll one line to make some space. We also mark - * it is an overrun so that if asked to really scroll later we know we already have. - * Note we only use one bit to indicate an overrun, so an overrun of more - * than one line will lead to some interesting scrolling and refreshing - * effects. - */ - - // Remove one line from the start - ep = gcw->buffer+gcw->bufpos; - for(p = gcw->buffer; p < ep && *p != '\n'; p++) { - #if GWIN_CONSOLE_ESCSEQ - if (*p == 27) - ESCtoAttr(p[1], &gcw->startattr); - #endif - } - - // Was there a newline? - if (*p != '\n') - p = gcw->buffer; // Oops - no newline, just delete one char - else - gcw->g.flags |= GCONSOLE_FLG_OVERRUN; // Mark the overrun - - // Delete the data - dp = ++p - gcw->buffer; // Calculate the amount to to be removed - gcw->bufpos -= dp; // Calculate the new size - if (gcw->bufpos) - memcpy(gcw->buffer, p, gcw->bufpos); // Move the rest of the data - } - - // Save the character - gcw->buffer[gcw->bufpos++] = c; - } - - /** - * Scroll the history buffer by one line - */ - static void scrollBuffer(GConsoleObject *gcw) { - char *p, *ep; - size_t dp; - - // Only scroll if we need to - if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE)) - return; - - // If a buffer overrun has been marked don't scroll as we have already - if ((gcw->g.flags & GCONSOLE_FLG_OVERRUN)) { - gcw->g.flags &= ~GCONSOLE_FLG_OVERRUN; - return; - } - - // Remove one line from the start - ep = gcw->buffer+gcw->bufpos; - for(p = gcw->buffer; p < ep && *p != '\n'; p++) { - #if GWIN_CONSOLE_ESCSEQ - if (*p == 27) - ESCtoAttr(p[1], &gcw->startattr); - #endif - } - - // Was there a newline, if not delete everything. - if (*p != '\n') { - gcw->bufpos = 0; - return; - } - - // Delete the data - dp = ++p - gcw->buffer; // Calculate the amount to to be removed - gcw->bufpos -= dp; // Calculate the new size - if (gcw->bufpos) - memcpy(gcw->buffer, p, gcw->bufpos); // Move the rest of the data - } - - /** - * Clear the history buffer - */ - static void clearBuffer(GConsoleObject *gcw) { - - // Only clear if we need to - if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE)) - return; - - gcw->bufpos = 0; - } - -#else - #define putCharInBuffer(gcw, c) - #define scrollBuffer(gcw) - #define clearBuffer(gcw) -#endif - -static void AfterClear(GWindowObject *gh) { - #define gcw ((GConsoleObject *)gh) - gcw->cx = 0; - gcw->cy = 0; - clearBuffer(gcw); - #if GWIN_CONSOLE_ESCSEQ - gcw->startattr = gcw->currattr; - #endif - #undef gcw -} - -static const gwinVMT consoleVMT = { - "Console", // The classname - sizeof(GConsoleObject), // The object size - #if GWIN_CONSOLE_USE_HISTORY - HistoryDestroy, // The destroy routine (custom) - HistoryRedraw, // The redraw routine (custom) - #else - 0, // The destroy routine - 0, // The redraw routine (default) - #endif - AfterClear, // The after-clear routine -}; - -GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *pInit) { - if (!(gc = (GConsoleObject *)_gwindowCreate(g, &gc->g, pInit, &consoleVMT, 0))) - return 0; - - #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM - gc->stream.vmt = &GWindowConsoleVMT; - #endif - - #if GWIN_CONSOLE_USE_HISTORY - gc->buffer = 0; - #if GWIN_CONSOLE_HISTORY_ATCREATE - gwinConsoleSetBuffer(&gc->g, TRUE); - #endif - #endif - - gc->cx = 0; - gc->cy = 0; - - #if GWIN_CONSOLE_ESCSEQ - gc->startattr = gc->currattr = 0; - gc->escstate = 0; - #endif - - gwinSetVisible((GHandle)gc, pInit->show); - - return (GHandle)gc; -} - -#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM - BaseSequentialStream *gwinConsoleGetStream(GHandle gh) { - if (gh->vmt != &consoleVMT) - return 0; - - return (BaseSequentialStream *)&(((GConsoleObject *)(gh))->stream); - } -#endif - -#if GWIN_CONSOLE_USE_HISTORY - bool_t gwinConsoleSetBuffer(GHandle gh, bool_t onoff) { - #define gcw ((GConsoleObject *)gh) - - if (gh->vmt != &consoleVMT) - return FALSE; - - // Do we want the buffer turned off? - if (!onoff) { - if (gcw->buffer) { - gfxFree(gcw->buffer); - gcw->buffer = 0; - } - return FALSE; - } - - // Is the buffer already on? - if (gcw->buffer) - return TRUE; - - // Get the number of characters that fit in the x direction - #if GWIN_CONSOLE_HISTORY_AVERAGING - gcw->bufsize = gh->width / ((2*gdispGetFontMetric(gh->font, fontMinWidth)+gdispGetFontMetric(gh->font, fontMaxWidth))/3); - #else - gcw->bufsize = gh->width / gdispGetFontMetric(gh->font, fontMinWidth); - #endif - gcw->bufsize++; // Allow space for a newline on each line. - - // Multiply by the number of lines - gcw->bufsize *= gh->height / gdispGetFontMetric(gh->font, fontHeight); - - // Allocate the buffer - if (!(gcw->buffer = gfxAlloc(gcw->bufsize))) - return FALSE; - - // All good! - gh->flags &= ~GCONSOLE_FLG_OVERRUN; - gcw->bufpos = 0; - return TRUE; - - #undef gcw - } -#endif - -/* - * We can get into gwinPutChar() 2 ways - - * 1. when the user calls us, and - * 2. when the redraw uses us to redraw the display. - * When called by option 2 we MUST not try to obtain a draw session - * as we already have one. - * - * We use these macro's below to make sure we do that safely - */ -#define DrawStart(gh) ((gh->flags & GCONSOLE_FLG_NOSTORE) || _gwinDrawStart(gh)) -#define DrawEnd(gh) { if (!(gh->flags & GCONSOLE_FLG_NOSTORE)) _gwinDrawEnd(gh); } - -void gwinPutChar(GHandle gh, char c) { - #define gcw ((GConsoleObject *)gh) - uint8_t width, fy; - - if (gh->vmt != &consoleVMT || !gh->font) - return; - - fy = gdispGetFontMetric(gh->font, fontHeight); - - #if GWIN_CONSOLE_ESCSEQ - /** - * Handle escape sequences - * 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 - */ - switch (gcw->escstate) { - case 1: - gcw->escstate = 0; - if (ESCtoAttr(c, &gcw->currattr)) { - if (gcw->cx == 0 && gcw->cy == 0) - gcw->startattr = gcw->currattr; - else { - putCharInBuffer(gcw, 27); - putCharInBuffer(gcw, c); - } - } else { - switch(c) { - case 'J': - // Clear the console and reset the cursor - clearBuffer(gcw); - if (DrawStart(gh)) { - gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); - DrawEnd(gh); - } - gcw->cx = 0; - gcw->cy = 0; - gcw->startattr = gcw->currattr; - break; - } - } - return; - } - #endif - - /** - * Special Characters: - * - * Carriage returns and line feeds (\r & \n) are handled in unix terminal cooked mode; that is, - * line feeds perform both actions and carriage-returns are ignored. - * - * if GWIN_CONSOLE_ESCSEQ is turned on then ESC is trapped ready for the escape command. - * - * All other characters are treated as printable. - */ - switch (c) { - case '\n': - // clear to the end of the line - #if GWIN_CONSOLE_USE_CLEAR_LINES - if (gcw->cx == 0 && gcw->cy+fy < gh->height && DrawStart(gh)) { - gdispGFillArea(gh->display, gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor); - DrawEnd(gh); - } - #endif - // update the cursor - gcw->cx = 0; - gcw->cy += fy; - putCharInBuffer(gcw, '\n'); - // We use lazy scrolling here and only scroll when the next char arrives - return; - - case '\r': - // gcw->cx = 0; - return; - - #if GWIN_CONSOLE_ESCSEQ - case 27: // ESC - gcw->escstate = 1; - return; - #endif - } - - // Characters with no width are ignored - if (!(width = gdispGetCharWidth(c, gh->font))) - return; - - // Allow space for (very crude) bold - #if GWIN_CONSOLE_ESCSEQ - if ((gcw->currattr & ESC_BOLD)) - width++; - #endif - - // Do we need to go to the next line to fit this character? - if (gcw->cx + width >= gh->width) { - gcw->cx = 0; - gcw->cy += fy; - putCharInBuffer(gcw, '\n'); - } - - // Do we need to scroll to fit this character? - if (gcw->cy + fy > gh->height) { - #if GWIN_CONSOLE_USE_HISTORY && GWIN_CONSOLE_BUFFER_SCROLLING - if (gcw->buffer) { - // Scroll the buffer and then redraw using the buffer - scrollBuffer(gcw); - if (DrawStart(gh)) { - HistoryRedraw(gh); - DrawEnd(gh); - } - } else - #endif - #if GDISP_NEED_SCROLL - { - // Scroll the console using hardware - scrollBuffer(gcw); - if (DrawStart(gh)) { - gdispGVerticalScroll(gh->display, gh->x, gh->y, gh->width, gh->height, fy, gh->bgcolor); - DrawEnd(gh); - } - - // Set the cursor to the start of the last line - gcw->cx = 0; - gcw->cy = (((coord_t)(gh->height/fy))-1)*fy; - } - #else - { - // Clear the console and reset the cursor - clearBuffer(gcw); - if (DrawStart(gh)) { - gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); - DrawEnd(gh); - } - gcw->cx = 0; - gcw->cy = 0; - #if GWIN_CONSOLE_ESCSEQ - gcw->startattr = gcw->currattr; - #endif - } - #endif - } - - // Save the char - putCharInBuffer(gcw, c); - - // Draw the character - if (DrawStart(gh)) { - - // If we are at the beginning of a new line clear the line - #if GWIN_CONSOLE_USE_CLEAR_LINES - if (gcw->cx == 0) - gdispGFillArea(gh->display, gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor); - #endif - - #if GWIN_CONSOLE_USE_FILLED_CHARS - gdispGFillChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw), gh->bgcolor); - #else - gdispGDrawChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw)); - #endif - - #if GWIN_CONSOLE_ESCSEQ - // Draw the underline - if ((gcw->currattr & ESC_UNDERLINE)) - gdispGDrawLine(gh->display, gh->x + gcw->cx, gh->y + gcw->cy + fy - gdispGetFontMetric(gh->font, fontDescendersHeight), - gh->x + gcw->cx + width + gdispGetFontMetric(gh->font, fontCharPadding), gh->y + gcw->cy + fy - gdispGetFontMetric(gh->font, fontDescendersHeight), - ESCPrintColor(gcw)); - // Bold (very crude) - if ((gcw->currattr & ESC_BOLD)) - gdispGDrawChar(gh->display, gh->x + gcw->cx + 1, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw)); - #endif - - DrawEnd(gh); - } - - // Update the cursor - gcw->cx += width + gdispGetFontMetric(gh->font, fontCharPadding); - - #undef gcw -} - -void gwinPutString(GHandle gh, const char *str) { - while(*str) - gwinPutChar(gh, *str++); -} - -void gwinPutCharArray(GHandle gh, const char *str, size_t n) { - while(n--) - gwinPutChar(gh, *str++); -} - -#include - -#define MAX_FILLER 11 -#define FLOAT_PRECISION 100000 - -static char *ltoa_wd(char *p, long num, unsigned radix, long divisor) { - int i; - char *q; - - if (!divisor) divisor = num; - - q = p + MAX_FILLER; - do { - i = (int)(num % radix); - i += '0'; - if (i > '9') - i += 'A' - '0' - 10; - *--q = i; - num /= radix; - } while ((divisor /= radix) != 0); - - i = (int)(p + MAX_FILLER - q); - do { - *p++ = *q++; - } while (--i); - - return p; -} - -#if GWIN_CONSOLE_USE_FLOAT - static char *ftoa(char *p, double num) { - long l; - unsigned long precision = FLOAT_PRECISION; - - l = num; - p = ltoa_wd(p, l, 10, 0); - *p++ = '.'; - l = (num - l) * precision; - return ltoa_wd(p, l, 10, precision / 10); - } -#endif - -void gwinPrintf(GHandle gh, const char *fmt, ...) { - va_list ap; - char *p, *s, c, filler; - int i, precision, width; - bool_t is_long, left_align; - long l; - #if GWIN_CONSOLE_USE_FLOAT - float f; - char tmpbuf[2*MAX_FILLER + 1]; - #else - char tmpbuf[MAX_FILLER + 1]; - #endif - - if (gh->vmt != &consoleVMT || !gh->font) - return; - - va_start(ap, fmt); - while (TRUE) { - c = *fmt++; - if (c == 0) { - va_end(ap); - return; - } - if (c != '%') { - gwinPutChar(gh, c); - continue; - } - - p = tmpbuf; - s = tmpbuf; - left_align = FALSE; - if (*fmt == '-') { - fmt++; - left_align = TRUE; - } - filler = ' '; - if (*fmt == '.') { - fmt++; - filler = '0'; - } - width = 0; - - while (TRUE) { - c = *fmt++; - if (c >= '0' && c <= '9') - c -= '0'; - else if (c == '*') - c = va_arg(ap, int); - else - break; - width = width * 10 + c; - } - precision = 0; - if (c == '.') { - while (TRUE) { - c = *fmt++; - if (c >= '0' && c <= '9') - c -= '0'; - else if (c == '*') - c = va_arg(ap, int); - else - break; - precision = precision * 10 + c; - } - } - /* Long modifier.*/ - if (c == 'l' || c == 'L') { - is_long = TRUE; - if (*fmt) - c = *fmt++; - } - else - is_long = (c >= 'A') && (c <= 'Z'); - - /* Command decoding.*/ - switch (c) { - case 'c': - filler = ' '; - *p++ = va_arg(ap, int); - break; - case 's': - filler = ' '; - if ((s = va_arg(ap, char *)) == 0) - s = "(null)"; - if (precision == 0) - precision = 32767; - for (p = s; *p && (--precision >= 0); p++); - break; - case 'D': - case 'd': - if (is_long) - l = va_arg(ap, long); - else - l = va_arg(ap, int); - if (l < 0) { - *p++ = '-'; - l = -l; - } - p = ltoa_wd(p, l, 10, 0); - break; - #if GWIN_CONSOLE_USE_FLOAT - case 'f': - f = (float) va_arg(ap, double); - if (f < 0) { - *p++ = '-'; - f = -f; - } - p = ftoa(p, f); - break; - #endif - case 'X': - case 'x': - c = 16; - goto unsigned_common; - case 'U': - case 'u': - c = 10; - goto unsigned_common; - case 'O': - case 'o': - c = 8; - unsigned_common: - if (is_long) - l = va_arg(ap, long); - else - l = va_arg(ap, int); - p = ltoa_wd(p, l, c, 0); - break; - default: - *p++ = c; - break; - } - - i = (int)(p - s); - if ((width -= i) < 0) - width = 0; - if (left_align == FALSE) - width = -width; - if (width < 0) { - if (*s == '-' && filler == '0') { - gwinPutChar(gh, *s++); - i--; - } - do { - gwinPutChar(gh, filler); - } while (++width != 0); - } - while (--i >= 0) - gwinPutChar(gh, *s++); - while (width) { - gwinPutChar(gh, filler); - width--; - } - } -} - -#endif /* GFX_USE_GWIN && GWIN_NEED_CONSOLE */ - - diff --git a/src/gwin/console.h b/src/gwin/console.h deleted file mode 100644 index 2a8f4a0a..00000000 --- a/src/gwin/console.h +++ /dev/null @@ -1,178 +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/console.h - * @brief GWIN Graphic window subsystem header file. - * - * @defgroup Console Console - * @ingroup Windows - * - * @details GWIN allows it to create a console/terminal like window. - * You can simply use chprintf() to print to the terminal. - * - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_CONSOLE must be set to TRUE in your gfxconf.h - * - * @{ - */ - -#ifndef _GWIN_CONSOLE_H -#define _GWIN_CONSOLE_H - -/* This file is included within "gwin/gwin.h" */ - -// A console window. Supports wrapped text writing and a cursor. -typedef struct GConsoleObject { - GWindowObject g; - coord_t cx, cy; // Cursor position - - #if GWIN_CONSOLE_ESCSEQ - uint8_t startattr; // ANSI-like escape sequences - uint8_t currattr; - uint16_t escstate; - #endif - - #if GWIN_CONSOLE_USE_HISTORY - char * buffer; // buffer to store console content - size_t bufsize; // size of buffer - size_t bufpos; // the position of the next char - #endif - - #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM - struct GConsoleWindowStream_t { - const struct GConsoleWindowVMT_t *vmt; - _base_asynchronous_channel_data - } stream; - #endif - -} GConsoleObject; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create a console window. - * @details A console window allows text to be written. - * @note Text in a console window supports newlines and will wrap text as required. - * @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] gc The GConsoleObject structure to initialise. If this is NULL the structure is dynamically allocated. - * @param[in] pInit The initialization parameters to use - * - * @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 On creation even if the window is visible it is not automatically cleared. - * You may do that by calling @p gwinClear() (possibly after changing your background color) - * @note A console does not save the drawing state. It is not automatically redrawn if the window is moved or - * its visibility state is changed. - * - * @api - */ -GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *pInit); -#define gwinConsoleCreate(gc, pInit) gwinGConsoleCreate(GDISP, gc, pInit) - -#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM - /** - * @brief Get a stream from a console window suitable for use with chprintf(). - * @return The stream handle or NULL if this is not a console window. - * - * @param[in] gh The window handle (must be a console window) - * - * @note Only useful in ChibiOS - * - * @api - */ - BaseSequentialStream *gwinConsoleGetStream(GHandle gh); -#endif - -#if GWIN_CONSOLE_USE_HISTORY - /** - * @brief Assign a buffer to keep track of the content while the widget is invisible. - * @pre GWIN_CONSOLE_USE_HISTORY must be set to TRUE in your gfxconf.h - * - * @param[in] gh The window handle (must be a console window) - * @param[in] onoff If TRUE a buffer is allocated to maintain console text - * when the console is obscured or invisible. If FALSE, then - * any existing buffer is deallocated. - * @note When the history buffer is turned on, scrolling is implemented using the - * history buffer. - * - * @return TRUE if the history buffer is now turned on. - */ - bool_t gwinConsoleSetBuffer(GHandle gh, bool_t onoff); -#endif - -/** - * @brief Put a character at the cursor position in the window. - * @note Uses the current foreground color to draw the character and fills the background using the background drawing color - * - * @param[in] gh The window handle (must be a console window) - * @param[in] c The character to draw - * - * @api - */ -void gwinPutChar(GHandle gh, char c); - -/** - * @brief Put a string at the cursor position in the window. It will wrap lines as required. - * @note Uses the current foreground color to draw the string and fills the background using the background drawing color - * - * @param[in] gh The window handle (must be a console window) - * @param[in] str The string to draw - * - * @api - */ -void gwinPutString(GHandle gh, const char *str); - -/** - * @brief Put the character array at the cursor position in the window. It will wrap lines as required. - * @note Uses the current foreground color to draw the string and fills the background using the background drawing color - * - * @param[in] gh The window handle (must be a console window) - * @param[in] str The string to draw - * @param[in] n The number of characters to draw - * - * @api - */ -void gwinPutCharArray(GHandle gh, const char *str, size_t n); - -/** - * @brief Print a formatted string at the cursor position in the window. It will wrap lines as required. - * @details This function implements a minimal printf() like functionality - * The general parameters format is: %[-][width|*][.precision|*][l|L]p. - * The following parameter types (p) are supported: - * - x hexadecimal integer. - * - X hexadecimal long. - * - o octal integer. - * - O octal long. - * - d decimal signed integer. - * - D decimal signed long. - * - u decimal unsigned integer. - * - U decimal unsigned long. - * - c character. - * - s string. - * @note Uses the current foreground color to draw the string and fills the background using the background drawing color - * - * @param[in] gh The window handle (must be a console window) - * @param[in] fmt The format string (as per printf) - * @param[in] ... The format string arguments. - * - * @api - */ -void gwinPrintf(GHandle gh, const char *fmt, ...); - -#ifdef __cplusplus -} -#endif - -#endif /* _GWIN_CONSOLE_H */ -/** @} */ diff --git a/src/gwin/frame.c b/src/gwin/frame.c deleted file mode 100644 index fd8fafea..00000000 --- a/src/gwin/frame.c +++ /dev/null @@ -1,322 +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/frame.c - * @brief GWIN sub-system frame code. - */ - -#include "gfx.h" - -#if GFX_USE_GWIN && GWIN_NEED_FRAME - -#include "src/gwin/class_gwin.h" - -/* Some position values */ -#define BUTTON_X 18 // Button Width -#define BUTTON_Y 18 // Button Height -#define BUTTON_I 3 // Button inner margin -#define BUTTON_T 2 // Gap from top of window to button -#define BUTTON_B 2 // Gap from button to the bottom of the frame title area -#define BORDER_L 2 // Left Border -#define BORDER_R 2 // Right Border -#define BORDER_T (BUTTON_Y+BUTTON_T+BUTTON_B) // Top Border (Title area) -#define BORDER_B 2 // Bottom Border - -/* Internal state flags */ -#define GWIN_FRAME_USER_FLAGS (GWIN_FRAME_CLOSE_BTN|GWIN_FRAME_MINMAX_BTN) -#define GWIN_FRAME_CLOSE_PRESSED (GWIN_FRAME_MINMAX_BTN << 1) -#define GWIN_FRAME_MIN_PRESSED (GWIN_FRAME_MINMAX_BTN << 2) -#define GWIN_FRAME_MAX_PRESSED (GWIN_FRAME_MINMAX_BTN << 3) -#define GWIN_FRAME_REDRAW_FRAME (GWIN_FRAME_MINMAX_BTN << 4) // Only redraw the frame -#if GWIN_FRAME_CLOSE_BTN < GWIN_FIRST_CONTROL_FLAG - #error "GWIN Frame: - Flag definitions don't match" -#endif -#if GWIN_FRAME_REDRAW_FRAME > GWIN_LAST_CONTROL_FLAG - #error "GWIN Frame: - Flag definitions don't match" -#endif - -static coord_t BorderSizeL(GHandle gh) { (void)gh; return BORDER_L; } -static coord_t BorderSizeR(GHandle gh) { (void)gh; return BORDER_R; } -static coord_t BorderSizeT(GHandle gh) { (void)gh; return BORDER_T; } -static coord_t BorderSizeB(GHandle gh) { (void)gh; return BORDER_B; } - -static void forceFrameRedraw(GWidgetObject *gw) { - // Force a redraw of just the frame. - // This is a big naughty but who really cares. - gw->g.flags |= GWIN_FRAME_REDRAW_FRAME; - gw->fnDraw(gw, gw->fnParam); - gw->g.flags &= ~GWIN_FRAME_REDRAW_FRAME; -} - -#if GINPUT_NEED_MOUSE - static void mouseDown(GWidgetObject *gw, coord_t x, coord_t y) { - coord_t pos; - - // We must be clicking on the frame button area to be of interest - if (y < BUTTON_T && y >= BUTTON_T+BUTTON_Y) - return; - - pos = gw->g.width - (BORDER_R+BUTTON_X); - if ((gw->g.flags & GWIN_FRAME_CLOSE_BTN)) { - if (x >= pos && x < pos+BUTTON_X) { - // Close is pressed - force redraw the frame only - gw->g.flags |= GWIN_FRAME_CLOSE_PRESSED; - forceFrameRedraw(gw); - return; - } - pos -= BUTTON_X; - } - if ((gw->g.flags & GWIN_FRAME_MINMAX_BTN)) { - if (x >= pos && x < pos+BUTTON_X) { - // Close is pressed - force redraw the frame only - gw->g.flags |= GWIN_FRAME_MAX_PRESSED; - forceFrameRedraw(gw); - return; - } - pos -= BUTTON_X; - if (x >= pos && x < pos+BUTTON_X) { - // Close is pressed - force redraw the frame only - gw->g.flags |= GWIN_FRAME_MIN_PRESSED; - forceFrameRedraw(gw); - return; - } - pos -= BUTTON_X; - } - } - - static void mouseUp(GWidgetObject *gw, coord_t x, coord_t y) { - #if GWIN_BUTTON_LAZY_RELEASE - if ((gw->g.flags & GWIN_FRAME_CLOSE_PRESSED)) { - // Close is released - destroy the window - gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); - forceFrameRedraw(gw); - _gwinSendEvent(&gw->g, GEVENT_GWIN_CLOSE); - gwinDestroy(&gw->g); - return; - } - if ((gw->g.flags & GWIN_FRAME_MAX_PRESSED)) { - // Max is released - maximize the window - gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); - forceFrameRedraw(gw); - gwinSetMinMax(&gw->g, GWIN_MAXIMIZE); - return; - } - if ((gw->g.flags & GWIN_FRAME_MIN_PRESSED)) { - // Min is released - minimize the window - gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); - forceFrameRedraw(gw); - gwinSetMinMax(&gw->g, GWIN_MINIMIZE); - return; - } - #else - // If nothing is pressed we have nothing to do. - if (!(gw->g.flags & (GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED))) - return; - - // We must be releasing over the button - if (y >= BUTTON_T && y < BUTTON_T+BUTTON_Y) { - coord_t pos; - - pos = gw->g.width - (BORDER_R+BUTTON_X); - if ((gw->g.flags & GWIN_FRAME_CLOSE_BTN)) { - if ((gw->g.flags & GWIN_FRAME_CLOSE_PRESSED) && x >= pos && x <= pos+BUTTON_X) { - // Close is released - destroy the window. This is tricky as we already have the drawing lock. - gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); - forceFrameRedraw(gw); - _gwinDestroy(&gw->g, REDRAW_INSESSION); - return; - } - pos -= BUTTON_X; - } - if ((gw->g.flags & GWIN_FRAME_MINMAX_BTN)) { - if ((gw->g.flags & GWIN_FRAME_MAX_PRESSED) && x >= pos && x <= pos+BUTTON_X) { - // Max is released - maximize the window - gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); - forceFrameRedraw(gw); - gwinSetMinMax(&gw->g, GWIN_MAXIMIZE); - return; - } - pos -= BUTTON_X; - if ((gw->g.flags & GWIN_FRAME_MIN_PRESSED) && x >= pos && x <= pos+BUTTON_X) { - // Min is released - minimize the window - gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); - forceFrameRedraw(gw); - gwinSetMinMax(&gw->g, GWIN_MINIMIZE); - return; - } - pos -= BUTTON_X; - } - } - - // Clear any flags and redraw the frame - gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); - forceFrameRedraw(gw); - #endif - } -#endif - -static const gcontainerVMT frameVMT = { - { - { - "Frame", // The classname - sizeof(GFrameObject), // The object size - _gcontainerDestroy, // The destroy routine - _gcontainerRedraw, // The redraw routine - 0, // The after-clear routine - }, - gwinFrameDraw_Std, // The default drawing routine - #if GINPUT_NEED_MOUSE - { - mouseDown, // Process mouse down event - mouseUp, // Process mouse up events - 0, // Process mouse move events - }, - #endif - #if GINPUT_NEED_TOGGLE - { - 0, // 1 toggle role - 0, // Assign Toggles - 0, // Get Toggles - 0, // Process toggle off events - 0, // Process toggle on events - }, - #endif - #if GINPUT_NEED_DIAL - { - 0, // 1 dial roles - 0, // Assign Dials - 0, // Get Dials - 0, // Process dial move events - }, - #endif - }, - BorderSizeL, // The size of the left border (mandatory) - BorderSizeT, // The size of the top border (mandatory) - BorderSizeR, // The size of the right border (mandatory) - BorderSizeB, // The size of the bottom border (mandatory) - 0, // A child has been added (optional) - 0, // A child has been deleted (optional) -}; - -GHandle gwinGFrameCreate(GDisplay *g, GFrameObject *fo, GWidgetInit *pInit, uint32_t flags) { - if (!(fo = (GFrameObject *)_gcontainerCreate(g, (GContainerObject *)fo, pInit, &frameVMT))) - return 0; - - // Make sure we only have "safe" flags. - flags &= GWIN_FRAME_USER_FLAGS; - - /* Apply flags. We apply these here so the controls above are outside the child area */ - fo->g.flags |= flags; - - gwinSetVisible(&fo->g, pInit->g.show); - - return &fo->g; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Default render routines // -/////////////////////////////////////////////////////////////////////////////////////////////////// - -void gwinFrameDraw_Transparent(GWidgetObject *gw, void *param) { - const GColorSet *pcol; - coord_t pos; - color_t contrast; - color_t btn; - (void)param; - - if (gw->g.vmt != (gwinVMT *)&frameVMT) - return; - - pcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled; - contrast = gdispContrastColor(pcol->edge); - btn = gdispBlendColor(pcol->edge, contrast, 128); - - // Render the frame - gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, BORDER_T, gw->text, gw->g.font, contrast, pcol->edge, justifyCenter); - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+BORDER_T, BORDER_L, gw->g.height-(BORDER_T+BORDER_B), pcol->edge); - gdispGFillArea(gw->g.display, gw->g.x+gw->g.width-BORDER_R, gw->g.y+BORDER_T, BORDER_R, gw->g.height-(BORDER_T+BORDER_B), pcol->edge); - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+gw->g.height-BORDER_B, gw->g.width, BORDER_B, pcol->edge); - - // Add the buttons - pos = gw->g.x+gw->g.width - (BORDER_R+BUTTON_X); - - if ((gw->g.flags & GWIN_FRAME_CLOSE_BTN)) { - if ((gw->g.flags & GWIN_FRAME_CLOSE_PRESSED)) - gdispFillArea(pos, gw->g.y+BUTTON_T, BUTTON_X, BUTTON_Y, btn); - gdispDrawLine(pos+BUTTON_I, gw->g.y+(BUTTON_T+BUTTON_I), pos+(BUTTON_X-BUTTON_I-1), gw->g.y+(BUTTON_T+BUTTON_Y-BUTTON_I-1), contrast); - gdispDrawLine(pos+(BUTTON_X-BUTTON_I-1), gw->g.y+(BUTTON_T+BUTTON_I), pos+BUTTON_I, gw->g.y+(BUTTON_T+BUTTON_Y-BUTTON_I-1), contrast); - pos -= BUTTON_X; - } - - if ((gw->g.flags & GWIN_FRAME_MINMAX_BTN)) { - if ((gw->g.flags & GWIN_FRAME_MAX_PRESSED)) - gdispFillArea(pos, gw->g.y+BUTTON_T, BUTTON_X, BUTTON_Y, btn); - // the symbol - gdispDrawBox(pos+BUTTON_I, gw->g.y+(BUTTON_T+BUTTON_I), BUTTON_X-2*BUTTON_I, BUTTON_Y-2*BUTTON_I, contrast); - gdispDrawLine(pos+(BUTTON_I+1), gw->g.y+(BUTTON_T+BUTTON_I+1), pos+(BUTTON_X-BUTTON_I-2), gw->g.y+(BUTTON_T+BUTTON_I+1), contrast); - gdispDrawLine(pos+(BUTTON_I+1), gw->g.y+(BUTTON_T+BUTTON_I+2), pos+(BUTTON_X-BUTTON_I-2), gw->g.y+(BUTTON_T+BUTTON_I+2), contrast); - pos -= BUTTON_X; - if ((gw->g.flags & GWIN_FRAME_MIN_PRESSED)) - gdispFillArea(pos, gw->g.y+BUTTON_T, BUTTON_X, BUTTON_Y, btn); - gdispDrawLine(pos+BUTTON_I, gw->g.y+(BUTTON_T+BUTTON_Y-BUTTON_I-1), pos+(BUTTON_X-BUTTON_I-1), gw->g.y+(BUTTON_T+BUTTON_Y-BUTTON_I-1), contrast); - pos -= BUTTON_X; - } - - // Don't touch the client area -} - -void gwinFrameDraw_Std(GWidgetObject *gw, void *param) { - (void)param; - - if (gw->g.vmt != (gwinVMT *)&frameVMT) - return; - - // Draw the frame - gwinFrameDraw_Transparent(gw, param); - - // Drop out if that is all we want to draw - if ((gw->g.flags & GWIN_FRAME_REDRAW_FRAME)) - return; - - // Draw the client area - gdispGFillArea(gw->g.display, gw->g.x + BORDER_L, gw->g.y + BORDER_T, gw->g.width - (BORDER_L+BORDER_R), gw->g.height - (BORDER_T+BORDER_B), gw->pstyle->background); -} - -#if GDISP_NEED_IMAGE - void gwinFrameDraw_Image(GWidgetObject *gw, void *param) { - #define gi ((gdispImage *)param) - coord_t x, y, iw, ih, mx, my; - - if (gw->g.vmt != (gwinVMT *)&frameVMT) - return; - - // Draw the frame - gwinFrameDraw_Transparent(gw, param); - - // Drop out if that is all we want to draw - if ((gw->g.flags & GWIN_FRAME_REDRAW_FRAME)) - return; - - // Draw the client area by tiling the image - mx = gw->g.x+gw->g.width - BORDER_R; - my = gw->g.y+gw->g.height - BORDER_B; - for(y = gw->g.y+BORDER_T, ih = gi->height; y < my; y += ih) { - if (ih > my - y) - ih = my - y; - for(x = gw->g.x+BORDER_L; iw = gi->width; x < mx; x += iw) { - if (iw > mx - x) - iw = mx - x; - gdispGImageDraw(gw->g.display, gi, x, y, ih, iw, 0, 0); - } - } - - #undef gi - } -#endif - -#endif /* (GFX_USE_GWIN && GWIN_NEED_FRAME) || defined(__DOXYGEN__) */ diff --git a/src/gwin/frame.h b/src/gwin/frame.h deleted file mode 100644 index 58e1fbc9..00000000 --- a/src/gwin/frame.h +++ /dev/null @@ -1,95 +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/frame.h - * @brief GWIN Graphic window subsystem header file. - * - * @defgroup Frame Frame - * @ingroup Containers - * - * @details A frame is a rectangular window that can have optional border as well as buttons to - * close, maximize and minimize it. The main purpose of this widget is to contain children. - * - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_FRAME must be set to TRUE in your gfxconf.h - * @{ - */ - -#ifndef _GWIN_FRAME_H -#define _GWIN_FRAME_H - -/** - * @brief Flags for gwinFrameCreate() - * @{ - */ -#define GWIN_FRAME_BORDER 0x00000000 // Deprecated. A border is always shown with a frame window now. -#define GWIN_FRAME_CLOSE_BTN 0x00000001 -#define GWIN_FRAME_MINMAX_BTN 0x00000002 -/** @} */ - -typedef GContainerObject GFrameObject; - -#ifdef __cplusplus -extern "C" { -#endif - - /** - * @brief Create a frame widget - * - * @details This widget provides a window like we know it from desktop systems. - * - * @param[in] g The GDisplay to display this window on - * @param[in] fo The GFrameObject structure to initialize. If this is NULL the structure is dynamically allocated. - * @param[in] pInit The initialization parameters - * @param[in] flags Some flags, see notes. - * - * @note Possible flags are: GWIN_FRAME_CLOSE_BTN, GWIN_FRAME_MINMAX_BTN. - * @note These frame buttons are processed internally. The close button will invoke a gwinDestroy() which will - * destroy the window itself and EVERY child it contains (also children of children). - * - * @return NULL if there is no resulting widget. A valid GHandle otherwise. - * - * @api - */ - GHandle gwinGFrameCreate(GDisplay *g, GFrameObject *fo, GWidgetInit *pInit, uint32_t flags); - #define gwinFrameCreate(fo, pInit, flags) gwinGFrameCreate(GDISP, fo, pInit, flags); - - /** - * @brief The custom draw routines for a frame window - * @details These function may be passed to @p gwinSetCustomDraw() to get different frame drawing styles - * - * @param[in] gw The widget object (in this case a frame) - * @param[in] param A parameter passed in from the user - * - * @note In your own custom drawing function you may optionally call these - * standard functions and then draw your extra details on top. - * - * @note gwinFrameDraw_Std() will fill the client area with the background color.
- * gwinFrameDraw_Transparent() will not fill the client area at all.
- * gwinFrameDraw_Image() will tile the image throughout the client area.
- * All these drawing functions draw the frame itself the same way. - * - * @note The standard functions below ignore the param parameter except for @p gwinFrameDraw_Image(). - * @note The image custom draw function @p gwinFrameDraw_Image() uses param to pass in the gdispImage pointer. - * The image must be already opened before calling @p gwinSetCustomDraw(). - * - * @api - * @{ - */ - void gwinFrameDraw_Std(GWidgetObject *gw, void *param); - void gwinFrameDraw_Transparent(GWidgetObject *gw, void *param); - void gwinFrameDraw_Image(GWidgetObject *gw, void *param); - /** @} */ - -#ifdef __cplusplus -} -#endif - -#endif /* _GWIN_FRAME_H */ -/** @} */ - diff --git a/src/gwin/gcontainer.c b/src/gwin/gcontainer.c deleted file mode 100644 index 97b5fabb..00000000 --- a/src/gwin/gcontainer.c +++ /dev/null @@ -1,201 +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/gcontainer.c - * @brief GWIN sub-system container code - */ - -#include "gfx.h" - -#if GFX_USE_GWIN && GWIN_NEED_CONTAINERS - -#include "src/gwin/class_gwin.h" - -void _gcontainerInit(void) -{ -} - -void _gcontainerDeinit(void) -{ -} - -GHandle _gcontainerCreate(GDisplay *g, GContainerObject *pgc, const GWidgetInit *pInit, const gcontainerVMT *vmt) { - if (!(pgc = (GContainerObject *)_gwidgetCreate(g, (GWidgetObject *)pgc, pInit, &vmt->gw))) - return 0; - - pgc->g.flags |= GWIN_FLG_CONTAINER; - - return &pgc->g; -} - -void _gcontainerDestroy(GHandle gh) { - GHandle child; - - while((child = gwinGetFirstChild(gh))) - gwinDestroy(child); - _gwidgetDestroy(gh); -} - -GHandle gwinGetFirstChild(GHandle gh) { - GHandle child; - - for(child = gwinGetNextWindow(0); child; child = gwinGetNextWindow(child)) - if (child->parent == gh) - return child; - return 0; -} - -GHandle gwinGetSibling(GHandle gh) { - GHandle child; - - for(child = gwinGetNextWindow(gh), gh = gh->parent; child; child = gwinGetNextWindow(child)) - if (child->parent == gh) - return child; - return 0; -} - -coord_t gwinGetInnerWidth(GHandle gh) { - if (!(gh->flags & GWIN_FLG_CONTAINER)) - return 0; - return gh->width - ((const gcontainerVMT *)gh->vmt)->LeftBorder(gh) - ((const gcontainerVMT *)gh->vmt)->RightBorder(gh); -} - -coord_t gwinGetInnerHeight(GHandle gh) { - if (!(gh->flags & GWIN_FLG_CONTAINER)) - return 0; - return gh->height - ((const gcontainerVMT *)gh->vmt)->TopBorder(gh) - ((const gcontainerVMT *)gh->vmt)->BottomBorder(gh); -} - -#endif /* GFX_USE_GWIN && GWIN_NEED_CONTAINERS */ -/** @} */ - -/*----------------------------------------------- - * The simplest container type - a container - *----------------------------------------------- - * - * @defgroup Containers Containers - * @ingroup GWIN - * - * @{ - */ - -#if GFX_USE_GWIN && GWIN_NEED_CONTAINER - -#if GWIN_CONTAINER_BORDER != GWIN_FIRST_CONTROL_FLAG - #error "GWIN Container: - Flag definitions don't match" -#endif - -#define BORDER_WIDTH 2 - -static coord_t BorderSize(GHandle gh) { return (gh->flags & GWIN_CONTAINER_BORDER) ? BORDER_WIDTH : 0; } - -// The container VMT table -static const gcontainerVMT containerVMT = { - { - { - "Container", // The classname - sizeof(GContainerObject), // The object size - _gcontainerDestroy, // The destroy routine - _gcontainerRedraw, // The redraw routine - 0, // The after-clear routine - }, - gwinContainerDraw_Std, // The default drawing routine - #if GINPUT_NEED_MOUSE - { - 0, 0, 0, // No mouse - }, - #endif - #if GINPUT_NEED_TOGGLE - { - 0, 0, 0, 0, 0, // No toggles - }, - #endif - #if GINPUT_NEED_DIAL - { - 0, 0, 0, 0, // No dials - }, - #endif - }, - BorderSize, // The size of the left border (mandatory) - BorderSize, // The size of the top border (mandatory) - BorderSize, // The size of the right border (mandatory) - BorderSize, // The size of the bottom border (mandatory) - 0, // A child has been added (optional) - 0, // A child has been deleted (optional) -}; - -GHandle gwinGContainerCreate(GDisplay *g, GContainerObject *gc, const GWidgetInit *pInit, uint32_t flags) { - if (!(gc = (GContainerObject *)_gcontainerCreate(g, gc, pInit, &containerVMT))) - return 0; - - gc->g.flags |= (flags & GWIN_CONTAINER_BORDER); - - gwinSetVisible((GHandle)gc, pInit->g.show); - return (GHandle)gc; -} - -void gwinContainerDraw_Transparent(GWidgetObject *gw, void *param) { - (void)param; - - if (gw->g.vmt != (gwinVMT *)&containerVMT) - return; - - if ((gw->g.flags & GWIN_CONTAINER_BORDER)) - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); - - // Don't touch the client area -} - -void gwinContainerDraw_Std(GWidgetObject *gw, void *param) { - (void)param; - - if (gw->g.vmt != (gwinVMT *)&containerVMT) - return; - - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); - gwinContainerDraw_Transparent(gw, param); -} - -#if GDISP_NEED_IMAGE - void gwinContainerDraw_Image(GWidgetObject *gw, void *param) { - #define gi ((gdispImage *)param) - coord_t x, y, iw, ih, mx, my; - - if (gw->g.vmt != (gwinVMT *)&containerVMT) - return; - - // Draw the frame - gwinContainerDraw_Transparent(gw, param); - - // Draw the client area by tiling the image - mx = gw->g.x+gw->g.width; - my = gw->g.y+gw->g.height; - y = gw->g.y; - if ((gw->g.flags & GWIN_CONTAINER_BORDER)) { - mx--; - my--; - y++; - } - for(ih=gi->height; y < my; y += ih) { - if (ih > my - y) - ih = my - y; - x = gw->g.x; - if ((gw->g.flags & GWIN_CONTAINER_BORDER)) - x++; - for(iw=gi->width; x < mx; x += iw) { - if (iw > mx - x) - iw = mx - x; - gdispGImageDraw(gw->g.display, gi, x, y, ih, iw, 0, 0); - } - } - - #undef gi - } -#endif - -#endif diff --git a/src/gwin/gcontainer.h b/src/gwin/gcontainer.h deleted file mode 100644 index 98707ede..00000000 --- a/src/gwin/gcontainer.h +++ /dev/null @@ -1,161 +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/gcontainer.h - * - * @defgroup Container Container - * @ingroup Containers - * - * @details A Container is a GWindow that supports child windows. It is also - * a widget in its own right and therefore can accept user input directly. - * - * @pre GFX_USE_GWIN and GWIN_NEED_CONTAINERS must be set to TRUE in your gfxconf.h - * @{ - */ - -#ifndef _GCONTAINER_H -#define _GCONTAINER_H - -/* This file is included within "gwin/gwin.h" */ - -// Forward definition -struct GContainerObject; - -/** - * @brief The GWIN Container structure - * @note A container is a GWIN widget that can have children. - * @note Do not access the members directly. Treat it as a black-box and use the method functions. - * - * @{ - */ -typedef GWidgetObject GContainerObject; -/** @} */ - -/** - * A comment/rant on the above structure: - * We would really like the GWidgetObject member to be anonymous. While this is - * allowed under the C11, C99, GNU and various other standards which have been - * around forever - compiler support often requires special flags e.g - * gcc requires the -fms-extensions flag (no wonder the language and compilers have - * not really progressed in 30 years). As portability is a key requirement - * we unfortunately won't use this useful feature in case we get a compiler that - * won't support it even with special flags. - */ - -#ifdef __cplusplus -extern "C" { -#endif - - /** - * @brief Get the first child window - * - * @return The first child or NULL if are no children windows - * - * @param[in] gh The parent container or NULL to get the first top level window - * - * @api - */ - GHandle gwinGetFirstChild(GHandle gh); - - /** - * @brief Get the next child window in the z-order - * - * @return The next window or NULL if no more children - * - * @param[in] gh The window to obtain the next sibling of. - * - * @note This returns the next window under the current parent window. - * Unlike @p gwinGetNextWindow() it will only return windows that - * have the same parent as the supplied window. - * - * @api - */ - GHandle gwinGetSibling(GHandle gh); - - /** - * @brief Get the inner width of a container window - * - * @return The inner width of a container window or zero if this is not a container - * - * @param[in] gh The window - * - * @api - */ - coord_t gwinGetInnerWidth(GHandle gh); - - /** - * @brief Get the inner height of a container window - * - * @return The inner height of a container window or zero if this is not a container - * - * @param[in] gh The window - * - * @api - */ - coord_t gwinGetInnerHeight(GHandle gh); - - - /** - * @brief Flags for gwinContainerCreate() - * @{ - */ - #define GWIN_CONTAINER_BORDER 0x00000001 - /** @} */ - - /** - * @brief Create a simple container. - * @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] gw The GContainerObject structure to initialise. If this is NULL the structure is dynamically allocated. - * @param[in] pInit The initialisation parameters - * @param[in] flags Some flags, see notes - * - * @api - */ - GHandle gwinGContainerCreate(GDisplay *g, GContainerObject *gw, const GWidgetInit *pInit, uint32_t flags); - #define gwinContainerCreate(gc, pInit, flags) gwinGContainerCreate(GDISP, gc, pInit, flags) - - /** - * @brief The custom draw routines for a simple container - * @details These function may be passed to @p gwinSetCustomDraw() to get different frame drawing styles - * - * @param[in] gw The widget object (in this case a frame) - * @param[in] param A parameter passed in from the user - * - * @note In your own custom drawing function you may optionally call these - * standard functions and then draw your extra details on top. - * - * @note gwinContainerDraw_Std() will fill the client area with the background color.
- * gwinContainerDraw_Transparent() will not fill the client area at all.
- * gwinContainerDraw_Image() will tile the image throughout the client area.
- * All these drawing functions draw the frame itself the same way. - * - * @note The standard functions below ignore the param parameter except for @p gwinContainerDraw_Image(). - * @note The image custom draw function @p gwinContainerDraw_Image() uses param to pass in the gdispImage pointer. - * The image must be already opened before calling @p gwinSetCustomDraw(). - * - * @api - * @{ - */ - void gwinContainerDraw_Std(GWidgetObject *gw, void *param); - void gwinContainerDraw_Transparent(GWidgetObject *gw, void *param); - void gwinContainerDraw_Image(GWidgetObject *gw, void *param); - /** @} */ - -#ifdef __cplusplus -} -#endif - -/* Include extra container types */ -#if GWIN_NEED_FRAME || defined(__DOXYGEN__) - #include "src/gwin/frame.h" -#endif - -#endif /* _GCONTAINER_H */ -/** @} */ diff --git a/src/gwin/gimage.c b/src/gwin/gimage.c deleted file mode 100644 index d5ca2c38..00000000 --- a/src/gwin/gimage.c +++ /dev/null @@ -1,162 +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/gimage.c - * @brief GWIN sub-system image code - */ - -#include "gfx.h" - -#if GFX_USE_GWIN && GWIN_NEED_IMAGE - -#include "src/gwin/class_gwin.h" - -#define widget(gh) ((GImageObject *)gh) - -static void _destroy(GWindowObject *gh) { - if (gdispImageIsOpen(&widget(gh)->image)) - gdispImageClose(&widget(gh)->image); -} - -#if GWIN_NEED_IMAGE_ANIMATION - static void _timer(void *param) { - _gwinUpdate((GHandle)param); - } -#endif - -static void _redraw(GHandle gh) { - coord_t x, y, w, h, dx, dy; - color_t bg; - #if GWIN_NEED_IMAGE_ANIMATION - delaytime_t delay; - #endif - - // The default display area - dx = 0; - dy = 0; - x = gh->x; - y = gh->y; - w = gh->width; - h = gh->height; - bg = gwinGetDefaultBgColor(); - - // If the image isn't open just clear the area - if (!gdispImageIsOpen(&widget(gh)->image)) { - gdispGFillArea(gh->display, x, y, w, h, bg); - return; - } - - // Center horizontally if the area is larger than the image - if (widget(gh)->image.width < w) { - w = widget(gh)->image.width; - dx = (gh->width-w)/2; - x += dx; - if (dx) - gdispGFillArea(gh->display, gh->x, y, dx, h, bg); - gdispGFillArea(gh->display, x+w, y, gh->width-dx-w, h, bg); - dx = 0; - } - - // Center image horizontally if the area is smaller than the image - else if (widget(gh)->image.width > w) { - dx = (widget(gh)->image.width - w)/2; - } - - // Center vertically if the area is larger than the image - if (widget(gh)->image.height < h) { - h = widget(gh)->image.height; - dy = (gh->height-h)/2; - y += dy; - if (dy) - gdispGFillArea(gh->display, x, gh->y, w, dy, bg); - gdispGFillArea(gh->display, x, y+h, w, gh->height-dy-h, bg); - dy = 0; - } - - // Center image vertically if the area is smaller than the image - else if (widget(gh)->image.height > h) { - dy = (widget(gh)->image.height - h)/2; - } - - // Reset the background color in case it has changed - gdispImageSetBgColor(&widget(gh)->image, bg); - - // Display the image - gdispGImageDraw(gh->display, &widget(gh)->image, x, y, w, h, dx, dy); - - #if GWIN_NEED_IMAGE_ANIMATION - // read the delay for the next frame - delay = gdispImageNext(&widget((GHandle)gh)->image); - - // Wait for that delay if required - switch(delay) { - case TIME_INFINITE: - // Everything is done - break; - case TIME_IMMEDIATE: - // We can't allow a continuous loop here as it would lock the system up so we delay for the minimum period - delay = 1; - // Fall through - default: - // Start the timer to draw the next frame of the animation - gtimerStart(&widget((GHandle)gh)->timer, _timer, (void*)gh, FALSE, delay); - break; - } - #endif -} - -static const gwinVMT imageVMT = { - "Image", // The class name - sizeof(GImageObject), // The object size - _destroy, // The destroy routine - _redraw, // The redraw routine - 0, // The after-clear routine -}; - -GHandle gwinGImageCreate(GDisplay *g, GImageObject *gobj, GWindowInit *pInit) { - if (!(gobj = (GImageObject *)_gwindowCreate(g, &gobj->g, pInit, &imageVMT, 0))) - return 0; - - // Ensure the gdispImageIsOpen() gives valid results - gdispImageInit(&gobj->image); - - // Initialise the timer - #if GWIN_NEED_IMAGE_ANIMATION - gtimerInit(&gobj->timer); - #endif - - gwinSetVisible((GHandle)gobj, pInit->show); - - return (GHandle)gobj; -} - -bool_t gwinImageOpenGFile(GHandle gh, GFILE *f) { - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&imageVMT) - return FALSE; - - if (gdispImageIsOpen(&widget(gh)->image)) - gdispImageClose(&widget(gh)->image); - - if ((gdispImageOpenGFile(&widget(gh)->image, f) & GDISP_IMAGE_ERR_UNRECOVERABLE)) - return FALSE; - - _gwinUpdate(gh); - - return TRUE; -} - -gdispImageError gwinImageCache(GHandle gh) { - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&imageVMT) - return GDISP_IMAGE_ERR_BADFORMAT; - - return gdispImageCache(&widget(gh)->image); -} - -#endif // GFX_USE_GWIN && GWIN_NEED_IMAGE diff --git a/src/gwin/gimage.h b/src/gwin/gimage.h deleted file mode 100644 index 0052f024..00000000 --- a/src/gwin/gimage.h +++ /dev/null @@ -1,127 +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/gimage.h - * @brief GWIN image widget header file. - * - * @defgroup ImageBox ImageBox - * @ingroup Widgets - * - * @details GWIN allos it to create an image widget. The widget - * takes no user input. - * - * @pre GFX_USE_GDISP must be set to TRUE in your gfxconf.h - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GDISP_NEED_IMAGE must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_IMAGE must be set to TRUE in your gfxconf.h - * @pre At least one image type must be enabled in your gfxconf.h - * - * @{ - */ - -#ifndef _GWIN_IMAGE_H -#define _GWIN_IMAGE_H - -// This file is included within "gwin/gwin.h" - -// An image window -typedef struct GImageObject { - GWindowObject g; - gdispImage image; // The image itself - #if GWIN_NEED_IMAGE_ANIMATION - GTimer timer; // Timer used for animated images - #endif -} GImageObject; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create an image widget. - * @details Display's a picture. - * @return NULL if there is no resultant drawing area, otherwise the widget handle. - * - * @param[in] g The GDisplay to display this window on - * @param[in] widget The image widget structure to initialise. If this is NULL, the structure is dynamically allocated. - * @param[in] pInit The initialization parameters to use. - * - * @note The default background color gets set to the current default one. - * @note An image window knows how to redraw. - * - * @api - */ -GHandle gwinGImageCreate(GDisplay *g, GImageObject *widget, GWindowInit *pInit); -#define gwinImageCreate(w, pInit) gwinGImageCreate(GDISP, w, pInit) - -/** - * @brief Opens the image using a GFILE - * @return TRUE if the image can be opened - * - * @param[in] gh The widget (must be an image widget) - * @param[in] f The open (for reading) GFILE to use - * - * @api - */ -bool_t gwinImageOpenGFile(GHandle gh, GFILE *f); - -/** - * @brief Opens the image using the specified filename - * @return TRUE if the open succeeds - * - * @param[in] gh The widget (must be an image widget) - * @param[in] filename The filename to open - * - * @api - */ -#define gwinImageOpenFile(gh, filename) gwinImageOpenGFile((gh), gfileOpen((filename), "rb")) - - /** - * @brief Sets the input routines that support reading the image from memory - * in RAM or flash. - * @pre GFILE_NEED_MEMFS must be TRUE - * @return TRUE if the IO open function succeeds - * - * @param[in] gh The widget (must be an image widget) - * @param[in] ptr A pointer to the image in RAM or Flash - * - * @api - */ -#define gwinImageOpenMemory(gh, ptr) gwinImageOpenGFile((gh), gfileOpenMemory((void *)(ptr), "rb")) - -/** - * @brief Sets the input routines that support reading the image from a BaseFileStream (eg. an SD-Card). - * @return TRUE if the IO open function succeeds - * @pre GFILE_NEED_CHIBIOSFS and GFX_USE_OS_CHIBIOS must be TRUE - * - * @param[in] gh The widget (must be an image widget) - * @param[in] streamPtr A pointer to the (open) BaseFileStream object. - * - * @api - */ -#define gwinImageOpenStream(gh, streamPtr) gwinImageOpenGFile((gh), gfileOpenBaseFIleStream((streamPtr), "rb")) - -/** - * @brief Cache the image. - * @details Decodes and caches the current frame into RAM. - * - * @param[in] gh The widget (must be an image widget) - * - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @api - */ -gdispImageError gwinImageCache(GHandle gh); - -#ifdef __cplusplus -} -#endif - -#endif // _GWIN_IMAGE_H -/** @} */ - diff --git a/src/gwin/graph.c b/src/gwin/graph.c deleted file mode 100644 index 4fcfad37..00000000 --- a/src/gwin/graph.c +++ /dev/null @@ -1,338 +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/graph.c - * @brief GWIN sub-system button code - */ - -#include "gfx.h" - -#if GFX_USE_GWIN && GWIN_NEED_GRAPH - -#include "src/gwin/class_gwin.h" - -#define GGRAPH_FLG_CONNECTPOINTS (GWIN_FIRST_CONTROL_FLAG<<0) -#define GGRAPH_ARROW_SIZE 5 - -static const GGraphStyle GGraphDefaultStyle = { - { GGRAPH_POINT_DOT, 0, White }, // point - { GGRAPH_LINE_DOT, 2, Gray }, // line - { GGRAPH_LINE_SOLID, 0, White }, // x axis - { GGRAPH_LINE_SOLID, 0, White }, // y axis - { GGRAPH_LINE_NONE, 0, White, 0 }, // x grid - { GGRAPH_LINE_NONE, 0, White, 0 }, // y grid - GWIN_GRAPH_STYLE_XAXIS_ARROWS|GWIN_GRAPH_STYLE_YAXIS_ARROWS // flags -}; - -static const gwinVMT graphVMT = { - "Graph", // The classname - sizeof(GGraphObject), // The object size - 0, // The destroy routine - 0, // The redraw routine - 0, // The after-clear routine -}; - -static void pointto(GGraphObject *gg, coord_t x, coord_t y, const GGraphPointStyle *style) { - if (style->type == GGRAPH_POINT_NONE) - return; - - // Convert to device space. Note the y-axis is inverted. - x += gg->g.x + gg->xorigin; - y = gg->g.y + gg->g.height - 1 - gg->yorigin - y; - - if (style->size <= 1) { - gdispGDrawPixel(gg->g.display, x, y, style->color); - return; - } - - switch(style->type) { - case GGRAPH_POINT_SQUARE: - gdispGDrawBox(gg->g.display, x-style->size, y-style->size, 2*style->size, 2*style->size, style->color); - break; -#if GDISP_NEED_CIRCLE - case GGRAPH_POINT_CIRCLE: - gdispGDrawCircle(gg->g.display, x, y, style->size, style->color); - break; -#endif - case GGRAPH_POINT_DOT: - default: - gdispGDrawPixel(gg->g.display, x, y, style->color); - break; - } -} - -static void lineto(GGraphObject *gg, coord_t x0, coord_t y0, coord_t x1, coord_t y1, const GGraphLineStyle *style) { - coord_t dy, dx; - coord_t addx, addy; - coord_t P, diff, i; - coord_t run_on, run_off, run; - - if (style->type == GGRAPH_LINE_NONE) - return; - - // Convert to device space. Note the y-axis is inverted. - x0 += gg->g.x + gg->xorigin; - y0 = gg->g.y + gg->g.height - 1 - gg->yorigin - y0; - x1 += gg->g.x + gg->xorigin; - y1 = gg->g.y + gg->g.height - 1 - gg->yorigin - y1; - - if (style->size <= 0) { - // Use the driver to draw a solid line - gdispGDrawLine(gg->g.display, x0, y0, x1, y1, style->color); - return; - } - - switch (style->type) { - case GGRAPH_LINE_DOT: - run_on = 1; - run_off = -style->size; - break; - - case GGRAPH_LINE_DASH: - run_on = style->size; - run_off = -style->size; - break; - - case GGRAPH_LINE_SOLID: - default: - // Use the driver to draw a solid line - gdispGDrawLine(gg->g.display, x0, y0, x1, y1, style->color); - return; - } - - // Use Bresenham's algorithm modified to draw a stylized line - run = 0; - if (x1 >= x0) { - dx = x1 - x0; - addx = 1; - } else { - dx = x0 - x1; - addx = -1; - } - if (y1 >= y0) { - dy = y1 - y0; - addy = 1; - } else { - dy = y0 - y1; - addy = -1; - } - - if (dx >= dy) { - dy *= 2; - P = dy - dx; - diff = P - dx; - - for(i=0; i<=dx; ++i) { - if (run++ >= 0) { - if (run >= run_on) - run = run_off; - gdispGDrawPixel(gg->g.display, x0, y0, style->color); - } - if (P < 0) { - P += dy; - x0 += addx; - } else { - P += diff; - x0 += addx; - y0 += addy; - } - } - } else { - dx *= 2; - P = dx - dy; - diff = P - dy; - - for(i=0; i<=dy; ++i) { - if (run++ >= 0) { - if (run >= run_on) - run = run_off; - gdispGDrawPixel(gg->g.display, x0, y0, style->color); - } - if (P < 0) { - P += dx; - y0 += addy; - } else { - P += diff; - x0 += addx; - y0 += addy; - } - } - } -} - -GHandle gwinGGraphCreate(GDisplay *g, GGraphObject *gg, const GWindowInit *pInit) { - if (!(gg = (GGraphObject *)_gwindowCreate(g, &gg->g, pInit, &graphVMT, 0))) - return 0; - gg->xorigin = gg->yorigin = 0; - gg->lastx = gg->lasty = 0; - gwinGraphSetStyle((GHandle)gg, &GGraphDefaultStyle); - gwinSetVisible((GHandle)gg, pInit->show); - return (GHandle)gg; -} - -void gwinGraphSetStyle(GHandle gh, const GGraphStyle *pstyle) { - #define gg ((GGraphObject *)gh) - - if (gh->vmt != &graphVMT) - return; - - gg->style.point = pstyle->point; - gg->style.line = pstyle->line; - gg->style.xaxis = pstyle->xaxis; - gg->style.yaxis = pstyle->yaxis; - gg->style.xgrid = pstyle->xgrid; - gg->style.ygrid = pstyle->ygrid; - gg->style.flags = pstyle->flags; - - #undef gg -} - -void gwinGraphSetOrigin(GHandle gh, coord_t x, coord_t y) { - #define gg ((GGraphObject *)gh) - - if (gh->vmt != &graphVMT) - return; - - gg->xorigin = x; - gg->yorigin = y; - - #undef gg -} - -void gwinGraphDrawAxis(GHandle gh) { - #define gg ((GGraphObject *)gh) - coord_t i, xmin, ymin, xmax, ymax; - - if (gh->vmt != &graphVMT || !_gwinDrawStart(gh)) - return; - - xmin = -gg->xorigin; - xmax = gh->width-gg->xorigin-1; - ymin = -gg->yorigin; - ymax = gh->height-gg->yorigin-1; - - // x grid - this code assumes that the GGraphGridStyle is a superset of GGraphListStyle - if (gg->style.xgrid.type != GGRAPH_LINE_NONE && gg->style.xgrid.spacing >= 2) { - for(i = gg->style.xgrid.spacing; i <= xmax; i += gg->style.xgrid.spacing) - lineto(gg, i, ymin, i, ymax, (GGraphLineStyle *)&gg->style.xgrid); - for(i = -gg->style.xgrid.spacing; i >= xmin; i -= gg->style.xgrid.spacing) - lineto(gg, i, ymin, i, ymax, (GGraphLineStyle *)&gg->style.xgrid); - } - - // y grid - this code assumes that the GGraphGridStyle is a superset of GGraphListStyle - if (gg->style.ygrid.type != GGRAPH_LINE_NONE && gg->style.ygrid.spacing >= 2) { - for(i = gg->style.ygrid.spacing; i <= ymax; i += gg->style.ygrid.spacing) - lineto(gg, xmin, i, xmax, i, (GGraphLineStyle *)&gg->style.ygrid); - for(i = -gg->style.ygrid.spacing; i >= ymin; i -= gg->style.ygrid.spacing) - lineto(gg, xmin, i, xmax, i, (GGraphLineStyle *)&gg->style.ygrid); - } - - // x axis - lineto(gg, xmin, 0, xmax, 0, &gg->style.xaxis); - if ((gg->style.flags & GWIN_GRAPH_STYLE_XAXIS_NEGATIVE_ARROWS)) { - if (xmin > 0 || xmin < -(GGRAPH_ARROW_SIZE+1)) { - lineto(gg, xmin, 0, xmin+GGRAPH_ARROW_SIZE, GGRAPH_ARROW_SIZE, &gg->style.xaxis); - lineto(gg, xmin, 0, xmin+GGRAPH_ARROW_SIZE, -GGRAPH_ARROW_SIZE, &gg->style.xaxis); - } - } - if ((gg->style.flags & GWIN_GRAPH_STYLE_XAXIS_POSITIVE_ARROWS)) { - if (xmax < 0 || xmax > (GGRAPH_ARROW_SIZE+1)) { - lineto(gg, xmax, 0, xmax-GGRAPH_ARROW_SIZE, GGRAPH_ARROW_SIZE, &gg->style.xaxis); - lineto(gg, xmax, 0, xmax-GGRAPH_ARROW_SIZE, -GGRAPH_ARROW_SIZE, &gg->style.xaxis); - } - } - - // y axis - lineto(gg, 0, ymin, 0, ymax, &gg->style.yaxis); - if ((gg->style.flags & GWIN_GRAPH_STYLE_YAXIS_NEGATIVE_ARROWS)) { - if (ymin > 0 || ymin < -(GGRAPH_ARROW_SIZE+1)) { - lineto(gg, 0, ymin, GGRAPH_ARROW_SIZE, ymin+GGRAPH_ARROW_SIZE, &gg->style.yaxis); - lineto(gg, 0, ymin, -GGRAPH_ARROW_SIZE, ymin+GGRAPH_ARROW_SIZE, &gg->style.yaxis); - } - } - if ((gg->style.flags & GWIN_GRAPH_STYLE_YAXIS_POSITIVE_ARROWS)) { - if (ymax < 0 || ymax > (GGRAPH_ARROW_SIZE+1)) { - lineto(gg, 0, ymax, GGRAPH_ARROW_SIZE, ymax-GGRAPH_ARROW_SIZE, &gg->style.yaxis); - lineto(gg, 0, ymax, -GGRAPH_ARROW_SIZE, ymax-GGRAPH_ARROW_SIZE, &gg->style.yaxis); - } - } - - _gwinDrawEnd(gh); - #undef gg -} - -void gwinGraphStartSet(GHandle gh) { - if (gh->vmt != &graphVMT) - return; - - gh->flags &= ~GGRAPH_FLG_CONNECTPOINTS; -} - -void gwinGraphDrawPoint(GHandle gh, coord_t x, coord_t y) { - #define gg ((GGraphObject *)gh) - - if (gh->vmt != &graphVMT || !_gwinDrawStart(gh)) - return; - - if ((gh->flags & GGRAPH_FLG_CONNECTPOINTS)) { - // Draw the line - lineto(gg, gg->lastx, gg->lasty, x, y, &gg->style.line); - - // Redraw the previous point because the line may have overwritten it - pointto(gg, gg->lastx, gg->lasty, &gg->style.point); - - } else - gh->flags |= GGRAPH_FLG_CONNECTPOINTS; - - // Save this point for next time. - gg->lastx = x; - gg->lasty = y; - - // Draw this point. - pointto(gg, x, y, &gg->style.point); - - _gwinDrawEnd(gh); - #undef gg -} - -void gwinGraphDrawPoints(GHandle gh, const point *points, unsigned count) { - #define gg ((GGraphObject *)gh) - unsigned i; - const point *p; - - if (gh->vmt != &graphVMT || !_gwinDrawStart(gh)) - return; - - // Draw the connecting lines - for(p = points, i = 0; i < count; p++, i++) { - if ((gh->flags & GGRAPH_FLG_CONNECTPOINTS)) { - // Draw the line - lineto(gg, gg->lastx, gg->lasty, p->x, p->y, &gg->style.line); - - // Redraw the previous point because the line may have overwritten it - if (i == 0) - pointto(gg, gg->lastx, gg->lasty, &gg->style.point); - - } else - gh->flags |= GGRAPH_FLG_CONNECTPOINTS; - - // Save this point for next time. - gg->lastx = p->x; - gg->lasty = p->y; - } - - - // Draw the points. - for(p = points, i = 0; i < count; p++, i++) - pointto(gg, p->x, p->y, &gg->style.point); - - _gwinDrawEnd(gh); - #undef gg -} - -#endif /* GFX_USE_GWIN && GWIN_NEED_GRAPH */ diff --git a/src/gwin/graph.h b/src/gwin/graph.h deleted file mode 100644 index be51f66c..00000000 --- a/src/gwin/graph.h +++ /dev/null @@ -1,186 +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/graph.h - * @brief GWIN GRAPH module header file - * - * @defgroup Graph Graph - * @ingroup Windows - * - * @details GWIN allows it to easily draw graphs - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_GRAPH must be set to TRUE in your gfxconf.h - * - * @{ - */ - -#ifndef _GWIN_GRAPH_H -#define _GWIN_GRAPH_H - -/* This file is included within "gwin/gwin.h" */ - -typedef enum GGraphPointType_e { - GGRAPH_POINT_NONE, GGRAPH_POINT_DOT, GGRAPH_POINT_SQUARE, GGRAPH_POINT_CIRCLE - } GGraphPointType; - -typedef struct GGraphPointStyle_t { - GGraphPointType type; - coord_t size; - color_t color; - } GGraphPointStyle; - -typedef enum GGraphLineType_e { - GGRAPH_LINE_NONE, GGRAPH_LINE_SOLID, GGRAPH_LINE_DOT, GGRAPH_LINE_DASH - } GGraphLineType; - -typedef struct GGraphLineStyle_t { - GGraphLineType type; - coord_t size; - color_t color; - } GGraphLineStyle; - -typedef struct GGraphGridStyle_t { - GGraphLineType type; - coord_t size; - color_t color; - coord_t spacing; - } GGraphGridStyle; - -typedef struct GGraphStyle_t { - GGraphPointStyle point; - GGraphLineStyle line; - GGraphLineStyle xaxis; - GGraphLineStyle yaxis; - GGraphGridStyle xgrid; - GGraphGridStyle ygrid; - uint16_t flags; - #define GWIN_GRAPH_STYLE_XAXIS_POSITIVE_ARROWS 0x0001 - #define GWIN_GRAPH_STYLE_XAXIS_NEGATIVE_ARROWS 0x0002 - #define GWIN_GRAPH_STYLE_YAXIS_POSITIVE_ARROWS 0x0004 - #define GWIN_GRAPH_STYLE_YAXIS_NEGATIVE_ARROWS 0x0008 - #define GWIN_GRAPH_STYLE_POSITIVE_AXIS_ARROWS (GWIN_GRAPH_STYLE_XAXIS_POSITIVE_ARROWS|GWIN_GRAPH_STYLE_YAXIS_POSITIVE_ARROWS) - #define GWIN_GRAPH_STYLE_NEGATIVE_AXIS_ARROWS (GWIN_GRAPH_STYLE_XAXIS_NEGATIVE_ARROWS|GWIN_GRAPH_STYLE_YAXIS_NEGATIVE_ARROWS) - #define GWIN_GRAPH_STYLE_XAXIS_ARROWS (GWIN_GRAPH_STYLE_XAXIS_POSITIVE_ARROWS|GWIN_GRAPH_STYLE_XAXIS_NEGATIVE_ARROWS) - #define GWIN_GRAPH_STYLE_YAXIS_ARROWS (GWIN_GRAPH_STYLE_YAXIS_POSITIVE_ARROWS|GWIN_GRAPH_STYLE_YAXIS_NEGATIVE_ARROWS) - #define GWIN_GRAPH_STYLE_ALL_AXIS_ARROWS (GWIN_GRAPH_STYLE_XAXIS_ARROWS|GWIN_GRAPH_STYLE_YAXIS_ARROWS) -} GGraphStyle; - -// A graph window -typedef struct GGraphObject { - GWindowObject g; - GGraphStyle style; - coord_t xorigin, yorigin; - coord_t lastx, lasty; - } GGraphObject; - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create a graph 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] gg The GGraphObject structure to initialise. If this is NULL the structure is dynamically allocated. - * @param[in] pInit The initialization parameters to use - * - * @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 The dimensions and position may be changed to fit on the real screen. - * @note A graph does not save the drawing state. It is not automatically redrawn if the window is moved or - * its visibility state is changed. - * @note The coordinate system within the window for graphing operations (but not for any other drawing - * operation) is relative to the bottom left corner and then shifted right and up by the specified - * graphing x and y origin. Note that this system is inverted in the y direction relative to the display. - * This gives the best graphing arrangement ie. increasing y values are closer to the top of the display. - * - * @api - */ -GHandle gwinGGraphCreate(GDisplay *g, GGraphObject *gg, const GWindowInit *pInit); -#define gwinGraphCreate(gg, pInit) gwinGGraphCreate(GDISP, gg, pInit) - -/** - * @brief Set the style of the graphing operations. - * - * @param[in] gh The window handle (must be a graph window) - * @param[in] pstyle The graph style to set. - * @note The graph is not automatically redrawn. The new style will apply to any new drawing operations. - * - * @api - */ -void gwinGraphSetStyle(GHandle gh, const GGraphStyle *pstyle); - -/** - * @brief Set the origin for graphing operations. - * - * @param[in] gh The window handle (must be a graph window) - * @param[in] x, y The new origin for the graph (in graph coordinates relative to the bottom left corner). - * @note The graph is not automatically redrawn. The new origin will apply to any new drawing operations. - * - * @api - */ -void gwinGraphSetOrigin(GHandle gh, coord_t x, coord_t y); - -/** - * @brief Draw the axis and the background grid. - * - * @param[in] gh The window handle (must be a graph window) - * @note The graph is not automatically cleared. You must do that first by calling gwinClear(). - * - * @api - */ -void gwinGraphDrawAxis(GHandle gh); - -/** - * @brief Start a new set of graphing data. - * @details This prevents a line being drawn from the last data point to the next point to be drawn. - * - * @param[in] gh The window handle (must be a graph window) - * - * @api - */ -void gwinGraphStartSet(GHandle gh); - -/** - * @brief Draw a graph point. - * @details A graph point and a line connecting to the previous point will be drawn. - * - * @param[in] gh The window handle (must be a graph window) - * @param[in] x, y The new point for the graph. - * - * @api - */ -void gwinGraphDrawPoint(GHandle gh, coord_t x, coord_t y); - -/** - * @brief Draw multiple graph points. - * @details A graph point and a line connecting to each previous point will be drawn. - * - * @param[in] gh The window handle (must be a graph window) - * @param[in] points The array of points for the graph. - * @param[in] count The number of points in the array. - * @note This is slightly more efficient than calling gwinGraphDrawPoint() repeatedly. - * - * @api - */ -void gwinGraphDrawPoints(GHandle gh, const point *points, unsigned count); - -#ifdef __cplusplus -} -#endif - -#endif /* _GWIN_GRAPH_H */ -/** @} */ - diff --git a/src/gwin/gwidget.c b/src/gwin/gwidget.c deleted file mode 100644 index e773dc6c..00000000 --- a/src/gwin/gwidget.c +++ /dev/null @@ -1,516 +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_GWIN && GWIN_NEED_WIDGET - -#include - -#include "src/gwin/class_gwin.h" - -/* Our listener for events for widgets */ -static GListener gl; - -/* Our default style - a white background theme */ -const GWidgetStyle WhiteWidgetStyle = { - HTML2COLOR(0xFFFFFF), // window background - - // enabled color set - { - HTML2COLOR(0x000000), // text - HTML2COLOR(0x404040), // edge - HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0xE0E0E0), // progress - inactive area - }, - - // disabled color set - { - HTML2COLOR(0xC0C0C0), // text - HTML2COLOR(0x808080), // edge - HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0xC0E0C0), // progress - active area - }, - - // pressed color set - { - HTML2COLOR(0x404040), // text - HTML2COLOR(0x404040), // edge - HTML2COLOR(0x808080), // fill - HTML2COLOR(0x00E000), // progress - active area - }, -}; - -/* Our black style */ -const GWidgetStyle BlackWidgetStyle = { - HTML2COLOR(0x000000), // window background - - // enabled color set - { - HTML2COLOR(0xC0C0C0), // text - HTML2COLOR(0xC0C0C0), // edge - HTML2COLOR(0x606060), // fill - HTML2COLOR(0x404040), // progress - inactive area - }, - - // disabled color set - { - HTML2COLOR(0x808080), // text - HTML2COLOR(0x404040), // edge - HTML2COLOR(0x404040), // fill - HTML2COLOR(0x004000), // progress - active area - }, - - // pressed color set - { - HTML2COLOR(0xFFFFFF), // text - HTML2COLOR(0xC0C0C0), // edge - HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0x008000), // progress - active area - }, -}; - -static const GWidgetStyle * defaultStyle = &BlackWidgetStyle; - -/* We use these everywhere in this file */ -#define gw ((GWidgetObject *)gh) -#define wvmt ((gwidgetVMT *)gh->vmt) - -/* Process an event */ -static void gwidgetEvent(void *param, GEvent *pe) { - #define pme ((GEventMouse *)pe) - #define pte ((GEventToggle *)pe) - #define pde ((GEventDial *)pe) - - GHandle h; - GHandle gh; - #if GFX_USE_GINPUT && (GINPUT_NEED_TOGGLE || GINPUT_NEED_DIAL) - uint16_t role; - #endif - (void) param; - - // Process various events - switch (pe->type) { - - #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE - case GEVENT_MOUSE: - case GEVENT_TOUCH: - // Cycle through all windows - for(gh = 0, h = gwinGetNextWindow(0); h; h = gwinGetNextWindow(h)) { - - // The window must be on this display and visible to be relevant - if (h->display != pme->display || !(h->flags & GWIN_FLG_SYSVISIBLE)) - continue; - - // Is the mouse currently captured by this widget? - if ((h->flags & (GWIN_FLG_WIDGET|GWIN_FLG_MOUSECAPTURE)) == (GWIN_FLG_WIDGET|GWIN_FLG_MOUSECAPTURE)) { - gh = h; - if ((pme->last_buttons & ~pme->current_buttons & GINPUT_MOUSE_BTN_LEFT)) { - gh->flags &= ~GWIN_FLG_MOUSECAPTURE; - if (wvmt->MouseUp) - wvmt->MouseUp(gw, pme->x - gh->x, pme->y - gh->y); - } else if (wvmt->MouseMove) - wvmt->MouseMove(gw, pme->x - gh->x, pme->y - gh->y); - - // There is only ever one captured mouse. Prevent normal mouse processing if there is a captured mouse - gh = 0; - break; - } - - // Save the highest z-order window that the mouse is over - if (pme->x >= h->x && pme->x < h->x + h->width && pme->y >= h->y && pme->y < h->y + h->height) - gh = h; - } - - // Process any mouse down over the highest order window if it is an enabled widget - if (gh && (gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) == (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) { - if ((~pme->last_buttons & pme->current_buttons & GINPUT_MOUSE_BTN_LEFT)) { - gh->flags |= GWIN_FLG_MOUSECAPTURE; - if (wvmt->MouseDown) - wvmt->MouseDown(gw, pme->x - gh->x, pme->y - gh->y); - } - } - break; - #endif - - #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE - case GEVENT_TOGGLE: - // Cycle through all windows - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - - // check if it a widget that is enabled and visible - if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) - continue; - - for(role = 0; role < wvmt->toggleroles; role++) { - if (wvmt->ToggleGet(gw, role) == pte->instance) { - if (pte->on) { - if (wvmt->ToggleOn) - wvmt->ToggleOn(gw, role); - } else { - if (wvmt->ToggleOff) - wvmt->ToggleOff(gw, role); - } - } - } - } - break; - #endif - - #if GFX_USE_GINPUT && GINPUT_NEED_DIAL - case GEVENT_DIAL: - // Cycle through all windows - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - - // check if it a widget that is enabled and visible - if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) - continue; - - for(role = 0; role < wvmt->dialroles; role++) { - if (wvmt->DialGet(gw, role) == pte->instance) { - if (wvmt->DialMove) - wvmt->DialMove(gw, role, pde->value, pde->maxvalue); - } - } - } - break; - #endif - - default: - break; - } - - #undef pme - #undef pte - #undef pde -} - -#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE - static GHandle FindToggleUser(uint16_t instance) { - GHandle gh; - uint16_t role; - - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if (!(gh->flags & GWIN_FLG_WIDGET)) // check if it a widget - continue; - - for(role = 0; role < wvmt->toggleroles; role++) { - if (wvmt->ToggleGet(gw, role) == instance) - return gh; - } - } - return 0; - } -#endif - -#if GFX_USE_GINPUT && GINPUT_NEED_DIAL - static GHandle FindDialUser(uint16_t instance) { - GHandle gh; - uint16_t role; - - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if (!(gh->flags & GWIN_FLG_WIDGET)) // check if it a widget - continue; - - for(role = 0; role < wvmt->dialroles; role++) { - if (wvmt->DialGet(gw, role) == instance) - return gh; - } - } - return 0; - } -#endif - -void _gwidgetInit(void) -{ - geventListenerInit(&gl); - geventRegisterCallback(&gl, gwidgetEvent, 0); -} - -void _gwidgetDeinit(void) -{ - /* ToDo */ -} - -GHandle _gwidgetCreate(GDisplay *g, GWidgetObject *pgw, const GWidgetInit *pInit, const gwidgetVMT *vmt) { - if (!(pgw = (GWidgetObject *)_gwindowCreate(g, &pgw->g, &pInit->g, &vmt->g, GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED))) - return 0; - - #if GWIN_NEED_COLLECTIONS - // This window can't be system enabled if the parent is not enabled - if (pgw->parent && !(pgw->parent->flags & GWIN_FLG_SYSENABLED)) - pgw->g.flags &= ~GWIN_FLG_SYSENABLED; - #endif - pgw->text = pInit->text ? pInit->text : ""; - pgw->fnDraw = pInit->customDraw ? pInit->customDraw : vmt->DefaultDraw; - pgw->fnParam = pInit->customParam; - pgw->pstyle = pInit->customStyle ? pInit->customStyle : defaultStyle; - #if GWIN_WIDGET_TAGS - pgw->tag = pInit->tag; - #endif - - return &pgw->g; -} - -void _gwidgetDestroy(GHandle gh) { - #if GFX_USE_GINPUT && (GINPUT_NEED_TOGGLE || GINPUT_NEED_DIAL) - uint16_t role, instance; - #endif - - // Deallocate the text (if necessary) - if ((gh->flags & GWIN_FLG_ALLOCTXT)) { - gh->flags &= ~GWIN_FLG_ALLOCTXT; - gfxFree((void *)gw->text); - } - - #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE - // Detach any toggles from this object - for(role = 0; role < wvmt->toggleroles; role++) { - instance = wvmt->ToggleGet(gw, role); - if (instance != GWIDGET_NO_INSTANCE) { - wvmt->ToggleAssign(gw, role, GWIDGET_NO_INSTANCE); - if (!FindToggleUser(instance)) - geventDetachSource(&gl, ginputGetToggle(instance)); - } - } - #endif - - #if GFX_USE_GINPUT && GINPUT_NEED_DIAL - // Detach any dials from this object - for(role = 0; role < wvmt->dialroles; role++) { - instance = wvmt->DialGet(gw, role); - if (instance != GWIDGET_NO_INSTANCE) { - wvmt->DialAssign(gw, role, GWIDGET_NO_INSTANCE); - if (!FindDialUser(instance)) - geventDetachSource(&gl, ginputGetDial(instance)); - } - } - #endif - - // Remove any listeners on this object. - geventDetachSourceListeners((GSourceHandle)gh); -} - -void _gwidgetRedraw(GHandle gh) { - if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) - return; - - gw->fnDraw(gw, gw->fnParam); -} - -void _gwinSendEvent(GHandle gh, GEventType type) { - GSourceListener * psl; - GEventGWin * pge; - - // Trigger a GWIN Event - psl = 0; - while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { - if (!(pge = (GEventGWin *)geventGetEventBuffer(psl))) - continue; - pge->type = type; - pge->gwin = gh; - #if GWIN_WIDGET_TAGS - pge->tag = (gh->flags & GWIN_FLG_WIDGET) ? ((GWidgetObject *)gh)->tag : 0; - #endif - geventSendEvent(psl); - } -} - -void gwinWidgetClearInit(GWidgetInit *pwi) { - char *p; - unsigned len; - - for(p = (char *)pwi, len = sizeof(GWidgetInit); len; len--) - *p++ = 0; -} - -void gwinSetDefaultStyle(const GWidgetStyle *pstyle, bool_t updateAll) { - if (!pstyle) - pstyle = &BlackWidgetStyle; - - if (updateAll) { - GHandle gh; - - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if ((gh->flags & GWIN_FLG_WIDGET) && ((GWidgetObject *)gh)->pstyle == defaultStyle) - gwinSetStyle(gh, pstyle); - else - gwinRedraw(gh); - } - } - gwinSetDefaultBgColor(pstyle->background); - defaultStyle = pstyle; -} - -/** - * @brief Get the current default style. - * - * @api - */ -const GWidgetStyle *gwinGetDefaultStyle(void) { - return defaultStyle; -} - - -void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) { - if (!(gh->flags & GWIN_FLG_WIDGET)) - return; - - // Dispose of the old string - if ((gh->flags & GWIN_FLG_ALLOCTXT)) { - gh->flags &= ~GWIN_FLG_ALLOCTXT; - if (gw->text) { - gfxFree((void *)gw->text); - gw->text = ""; - } - } - - // Alloc the new text if required - if (!text || !*text) - gw->text = ""; - else if (useAlloc) { - char *str; - - if ((str = gfxAlloc(strlen(text)+1))) { - gh->flags |= GWIN_FLG_ALLOCTXT; - strcpy(str, text); - } - gw->text = (const char *)str; - } else - gw->text = text; - _gwinUpdate(gh); -} - -const char *gwinGetText(GHandle gh) { - if (!(gh->flags & GWIN_FLG_WIDGET)) - return 0; - - return gw->text; -} - -void gwinSetStyle(GHandle gh, const GWidgetStyle *pstyle) { - if (!(gh->flags & GWIN_FLG_WIDGET)) - return; - gw->pstyle = pstyle ? pstyle : defaultStyle; - gh->bgcolor = pstyle->background; - gh->color = pstyle->enabled.text; - _gwinUpdate(gh); -} - -const GWidgetStyle *gwinGetStyle(GHandle gh) { - if (!(gh->flags & GWIN_FLG_WIDGET)) - return 0; - - return gw->pstyle; -} - -void gwinSetCustomDraw(GHandle gh, CustomWidgetDrawFunction fn, void *param) { - if (!(gh->flags & GWIN_FLG_WIDGET)) - return; - - gw->fnDraw = fn ? fn : wvmt->DefaultDraw; - gw->fnParam = param; - _gwinUpdate(gh); -} - -bool_t gwinAttachListener(GListener *pl) { - return geventAttachSource(pl, GWIDGET_SOURCE, 0); -} - -#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE - bool_t gwinAttachMouse(uint16_t instance) { - GSourceHandle gsh; - - if (!(gsh = ginputGetMouse(instance))) - return FALSE; - - return geventAttachSource(&gl, gsh, GLISTEN_MOUSEMETA|GLISTEN_MOUSEDOWNMOVES); - } -#endif - -#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE - bool_t gwinAttachToggle(GHandle gh, uint16_t role, uint16_t instance) { - GSourceHandle gsh; - uint16_t oi; - - // Is this a widget - if (!(gh->flags & GWIN_FLG_WIDGET)) - return FALSE; - - // Is the role valid - if (role >= wvmt->toggleroles) - return FALSE; - - // Is this a valid device - if (!(gsh = ginputGetToggle(instance))) - return FALSE; - - // Is this already done? - oi = wvmt->ToggleGet(gw, role); - if (instance == oi) - return TRUE; - - // Remove the old instance - if (oi != GWIDGET_NO_INSTANCE) { - wvmt->ToggleAssign(gw, role, GWIDGET_NO_INSTANCE); - if (!FindToggleUser(oi)) - geventDetachSource(&gl, ginputGetToggle(oi)); - } - - // Assign the new - wvmt->ToggleAssign(gw, role, instance); - return geventAttachSource(&gl, gsh, GLISTEN_TOGGLE_ON|GLISTEN_TOGGLE_OFF); - } -#endif - -#if GFX_USE_GINPUT && GINPUT_NEED_DIAL - bool_t gwinAttachDial(GHandle gh, uint16_t role, uint16_t instance) { - GSourceHandle gsh; - uint16_t oi; - - if (!(gh->flags & GWIN_FLG_WIDGET)) - return FALSE; - - // Is the role valid - if (role >= wvmt->dialroles) - return FALSE; - - // Is this a valid device - if (!(gsh = ginputGetDial(instance))) - return FALSE; - - // Is this already done? - oi = wvmt->DialGet(gw, role); - if (instance == oi) - return TRUE; - - // Remove the old instance - if (oi != GWIDGET_NO_INSTANCE) { - wvmt->DialAssign(gw, role, GWIDGET_NO_INSTANCE); - if (!FindDialUser(oi)) - geventDetachSource(&gl, ginputGetDial(oi)); - } - - // Assign the new - wvmt->DialAssign(gw, role, instance); - return geventAttachSource(&gl, gsh, 0); - } -#endif - -#if GWIN_WIDGET_TAGS - void gwinSetTag(GHandle gh, WidgetTag tag) { - if ((gh->flags & GWIN_FLG_WIDGET)) - gw->tag = tag; - } - - WidgetTag gwinGetTag(GHandle gh) { - return ((gh->flags & GWIN_FLG_WIDGET)) ? gw->tag : 0; - } -#endif - -#endif /* GFX_USE_GWIN && GWIN_NEED_WIDGET */ -/** @} */ diff --git a/src/gwin/gwidget.h b/src/gwin/gwidget.h deleted file mode 100644 index 18b42b79..00000000 --- a/src/gwin/gwidget.h +++ /dev/null @@ -1,391 +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/gwidget.h - * @brief GWIN Widgets header file. - * - * @defgroup Widget Widget - * @ingroup Widgets - * - * @details A widget is a Window that supports interacting with the user - * via an input device such as a mouse or toggle buttons. It is the - * base class for widgets such as buttons and sliders. - * - * @pre GFX_USE_GWIN and GWIN_NEED_WIDGET must be set to TRUE in your gfxconf.h - * @{ - */ - -#ifndef _GWIDGET_H -#define _GWIDGET_H - -/* This file is included within "gwin/gwin.h" */ - -// Forward definition -struct GWidgetObject; - -/** - * @brief The GColorSet structure - * @{ - */ -typedef struct GColorSet { - color_t text; // @< The text color - color_t edge; // @< The edge color - color_t fill; // @< The fill color - color_t progress; // @< The color of progress bars -} GColorSet; -/** @} */ - -/** - * @brief The GWidgetStyle structure - * @details A GWidgetStyle is a set of colors that together form a "style". - * These colors should not be confused with the GWindow foreground - * and background colors which are used for drawing operations. - * @{ - */ -typedef struct GWidgetStyle { - color_t background; // @< The window background color - GColorSet enabled; // @< The colors when enabled - GColorSet disabled; // @< The colors when disabled - GColorSet pressed; // @< The colors when pressed -} GWidgetStyle; -/** @} */ - -/** - * @brief We define a couple of GWidgetStyle's that you can use in your - * application. The Black style is the default style if you don't - * specify one. - * @note BlackWidgetStyle means that it is designed for a Black background. - * Similarly WhiteWidgetStyle is designed for a White background. - * @{ - */ -extern const GWidgetStyle BlackWidgetStyle; -extern const GWidgetStyle WhiteWidgetStyle; -/** @} */ - -/** - * @brief Defines a custom drawing function for a widget - */ -typedef void (*CustomWidgetDrawFunction)(struct GWidgetObject *gw, void *param); - -/** - * @brief Defines a the type of a tag on a widget - */ -typedef uint16_t WidgetTag; - -/** - * @brief The structure to initialise a widget. - * - * @note Some widgets may have extra parameters. - * @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 gwinWidgetClearInit() - * to clear it. - * @note The text element must be static string (not stack allocated). If you want to use - * a dynamic string (eg a stack allocated string) use NULL for this member and then call - * @p gwinSetText() with useAlloc set to TRUE. - * - * @{ - */ -typedef struct GWidgetInit { - GWindowInit g; // @< The GWIN initializer - const char * text; // @< The initial text - CustomWidgetDrawFunction customDraw; // @< A custom draw function - use NULL for the standard - void * customParam; // @< A parameter for the custom draw function (default = NULL) - const GWidgetStyle * customStyle; // @< A custom style to use - use NULL for the default style - #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) - WidgetTag tag; // @< The tag to associate with the widget - #endif -} GWidgetInit; -/** @} */ - -/** - * @brief The GWIN Widget structure - * @note A widget is a GWIN window that accepts user input. - * It also has a number of other properties such as its ability - * to redraw itself (a widget maintains drawing state). - * @note Do not access the members directly. Treat it as a black-box and use the method functions. - * - * @{ - */ -typedef struct GWidgetObject { - GWindowObject g; // @< This is still a GWIN - const char * text; // @< The widget text - CustomWidgetDrawFunction fnDraw; // @< The current draw function - void * fnParam; // @< A parameter for the current draw function - const GWidgetStyle * pstyle; // @< The current widget style colors - #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) - WidgetTag tag; // @< The widget tag - #endif -} GWidgetObject; -/** @} */ - -/** - * A comment/rant on the above structure: - * We would really like the GWindowObject member to be anonymous. While this is - * allowed under the C11, C99, GNU and various other standards which have been - * around forever - compiler support often requires special flags e.g - * gcc requires the -fms-extensions flag (no wonder the language and compilers have - * not really progressed in 30 years). As portability is a key requirement - * we unfortunately won't use this useful feature in case we get a compiler that - * won't support it even with special flags. - */ - -/** - * @brief A Generic GWIN Event - * @note All gwin windows when sending events will either use this structure or a - * structure that is 100% compatible except that it may also have extra fields. - * @note There are currently no GEventGWin listening flags - use 0 as the flags to @p gwinAttachListener() - * - * @{ - */ -typedef struct GEventGWin { - GEventType type; // The type of this event - GHandle gwin; // The gwin window handle - #if GWIN_NEED_WIDGET && GWIN_WIDGET_TAGS - WidgetTag tag; // The tag (if applicable) - #endif -} GEventGWin; -/** @} */ - -/** - * @brief The list of predefined GWIN events. - * @note The definition of an event type does not mean it is always sent. For example, - * close events are sent by Frame windows but by little else. They are normally - * only sent if there is a specific reason that the event should be sent. - * @{ - */ -#define GEVENT_GWIN_OPEN (GEVENT_GWIN_FIRST+0x00) -#define GEVENT_GWIN_CLOSE (GEVENT_GWIN_FIRST+0x01) -#define GEVENT_GWIN_RESIZE (GEVENT_GWIN_FIRST+0x02) -#define GEVENT_GWIN_CTRL_FIRST (GEVENT_GWIN_FIRST+0x40) -/** @} */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Clear a GWidgetInit 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 GWidgetInit structure to clear - * - * @api - */ -void gwinWidgetClearInit(GWidgetInit *pwi); - -/** - * @brief Set the default style for widgets created hereafter. - * - * @param[in] pstyle The default style. Passing NULL uses the system compiled style. - * @param[in] updateAll If TRUE then all existing widgets that are using the current default style - * will be updated to use this new style. Widgets that have custom styles different - * from the default style will not be updated. - * - * @note The style must be allocated statically (not on the stack) as only the pointer is stored. - * - * @api - */ -void gwinSetDefaultStyle(const GWidgetStyle *pstyle, bool_t updateAll); - -/** - * @brief Get the current default style. - * - * @return The current default style. - * - * @api - */ -const GWidgetStyle *gwinGetDefaultStyle(void); - -/** - * @brief Set the text of a widget. - * - * @param[in] gh The widget handle - * @param[in] text The text to set. This must be a constant string unless useAlloc is set. - * @param[in] useAlloc If TRUE the string specified will be copied into dynamically allocated memory. - * - * @note The widget is automatically redrawn - * @note Non-widgets will ignore this call. - * - * @api - */ -void gwinSetText(GHandle gh, const char *text, bool_t useAlloc); - -/** - * @brief Get the text of a widget. - * @return The widget text or NULL if it isn't a widget - * - * @param[in] gh The widget handle - * - * @api - */ -const char *gwinGetText(GHandle gh); - -#if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) - /** - * @brief Set the tag of a widget. - * - * @param[in] gh The widget handle - * @param[in] tag The tag to set. - * - * @note Non-widgets will ignore this call. - * - * @pre Requires GWIN_WIDGET_TAGS to be TRUE - * - * @api - */ - void gwinSetTag(GHandle gh, WidgetTag tag); - - /** - * @brief Get the tag of a widget. - * @return The widget tag value (or 0 if it is not a widget) - * - * @param[in] gh The widget handle - * - * @pre Requires GWIN_WIDGET_TAGS to be TRUE - * - * @api - */ - WidgetTag gwinGetTag(GHandle gh); -#endif - -/** - * @brief Set the style of a widget. - * - * @param[in] gh The widget handle - * @param[in] pstyle The style to set. This must be a static structure (not allocated on a transient stack). - * Use NULL to reset to the default style. - * - * @note The widget is automatically redrawn - * @note Non-widgets will ignore this call. - * - * @api - */ -void gwinSetStyle(GHandle gh, const GWidgetStyle *pstyle); - -/** - * @brief Get the style of a widget. - * @return The widget style or NULL if it isn't a widget - * - * @param[in] gh The widget handle - * - * @api - */ -const GWidgetStyle *gwinGetStyle(GHandle gh); - -/** - * @brief Set the routine to perform a custom widget drawing. - * - * @param[in] gh The widget handle - * @param[in] fn The function to use to draw the widget - * @param[in] param A parameter to pass to the widget drawing function - * - * @note The widget is not automatically redrawn. Call @p gwinDraw() to redraw the widget. - * @note Non-widgets will ignore this call. - * - * @api - */ -void gwinSetCustomDraw(GHandle gh, CustomWidgetDrawFunction fn, void *param); - -/** - * @brief Attach a Listener to listen for widget events - * @return TRUE on success - * - * @param[in] pl The listener - * - * @api - */ -bool_t gwinAttachListener(GListener *pl); - -#if (GFX_USE_GINPUT && GINPUT_NEED_MOUSE) || defined(__DOXYGEN__) - /** - * @brief Set the mouse to be used to control the widgets - * @return TRUE on success - * - * @param[in] instance The mouse instance - * - * @note Every widget uses the same mouse. - * - * @api - */ - bool_t gwinAttachMouse(uint16_t instance); -#endif - -#if (GFX_USE_GINPUT && GINPUT_NEED_TOGGLE) || defined(__DOXYGEN__) - /** - * @brief Attach a toggle to a widget - * @return TRUE on success - * - * @param[in] gh The widget handle - * @param[in] role The function the toggle will perform for the widget - * @param[in] instance The toggle instance - * - * @note See the documentation on the specific widget to see the possible - * values for the role parameter. If it is out of range, this function - * will return FALSE - * - * @api - */ - bool_t gwinAttachToggle(GHandle gh, uint16_t role, uint16_t instance); -#endif - -#if (GFX_USE_GINPUT && GINPUT_NEED_DIAL) || defined(__DOXYGEN__) - /** - * @brief Attach a toggle to a widget - * @return TRUE on success - * - * @param[in] gh The widget handle - * @param[in] role The function the dial will perform for the widget - * @param[in] instance The dial instance - * - * @note See the documentation on the specific widget to see the possible - * values for the role parameter. If it is out of range, this function - * will return FALSE - * - * @api - */ - bool_t gwinAttachDial(GHandle gh, uint16_t role, uint16_t instance); -#endif - -#ifdef __cplusplus -} -#endif - -/* Include extra widget types */ -#if GWIN_NEED_BUTTON || defined(__DOXYGEN__) - #include "src/gwin/button.h" -#endif - -#if GWIN_NEED_SLIDER || defined(__DOXYGEN__) - #include "src/gwin/slider.h" -#endif - -#if GWIN_NEED_CHECKBOX || defined(__DOXYGEN__) - #include "src/gwin/checkbox.h" -#endif - -#if GWIN_NEED_RADIO || defined(__DOXYGEN__) - #include "src/gwin/radio.h" -#endif - -#if GWIN_NEED_LABEL || defined(__DOXYGEN__) - #include "src/gwin/label.h" -#endif - -#if GWIN_NEED_LIST || defined(__DOXYGEN__) - #include "src/gwin/list.h" -#endif - -#if GWIN_NEED_PROGRESSBAR || defined(__DOXYGEN__) - #include "src/gwin/progressbar.h" -#endif - -#endif /* _GWIDGET_H */ -/** @} */ diff --git a/src/gwin/gwin.c b/src/gwin/gwin.c deleted file mode 100644 index b5b7ad6b..00000000 --- a/src/gwin/gwin.c +++ /dev/null @@ -1,375 +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_GWIN - -#include "src/gwin/class_gwin.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 - - 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_PIXELREAD - color_t gwinGetPixelColor(GHandle gh, coord_t x, coord_t y) { - if (!_gwinDrawStart(gh)) return; - 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_button.c b/src/gwin/gwin_button.c new file mode 100644 index 00000000..35627cfb --- /dev/null +++ b/src/gwin/gwin_button.c @@ -0,0 +1,328 @@ +/* + * 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_button.c + * @brief GWIN sub-system button code + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_BUTTON + +#include "gwin_class.h" + +// Parameters for various shapes +#define RND_CNR_SIZE 5 // Rounded corner size for rounded buttons +#define ARROWHEAD_DIVIDER 4 // A quarter of the height for the arrow head +#define ARROWBODY_DIVIDER 4 // A quarter of the width for the arrow body +#define TOP_FADE 50 // (TOP_FADE/255)% fade to white for top of button +#define BOTTOM_FADE 25 // (BOTTOM_FADE/255)% fade to black for bottom of button + +// Our pressed state +#define GBUTTON_FLG_PRESSED (GWIN_FIRST_CONTROL_FLAG<<0) + +#if GINPUT_NEED_MOUSE + // A mouse down has occurred over the button + static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { + (void) x; (void) y; + gw->g.flags |= GBUTTON_FLG_PRESSED; + _gwinUpdate((GHandle)gw); + } + + // A mouse up has occurred (it may or may not be over the button) + static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) { + (void) x; (void) y; + gw->g.flags &= ~GBUTTON_FLG_PRESSED; + _gwinUpdate((GHandle)gw); + + #if !GWIN_BUTTON_LAZY_RELEASE + // If the mouse up was not over the button then cancel the event + if (x < 0 || y < 0 || x >= gw->g.width || y >= gw->g.height) + return; + #endif + + _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON); + } +#endif + +#if GINPUT_NEED_TOGGLE + // A toggle off has occurred + static void ToggleOff(GWidgetObject *gw, uint16_t role) { + (void) role; + gw->g.flags &= ~GBUTTON_FLG_PRESSED; + _gwinUpdate((GHandle)gw); + } + + // A toggle on has occurred + static void ToggleOn(GWidgetObject *gw, uint16_t role) { + (void) role; + gw->g.flags |= GBUTTON_FLG_PRESSED; + _gwinUpdate((GHandle)gw); + // Trigger the event on button down (different than for mouse/touch) + _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON); + } + + static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { + (void) role; + ((GButtonObject *)gw)->toggle = instance; + } + + static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { + (void) role; + return ((GButtonObject *)gw)->toggle; + } +#endif + +// The button VMT table +static const gwidgetVMT buttonVMT = { + { + "Button", // The classname + sizeof(GButtonObject), // The object size + _gwidgetDestroy, // The destroy routine + _gwidgetRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinButtonDraw_Normal, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + MouseDown, // Process mouse down events + MouseUp, // Process mouse up events + 0, // Process mouse move events (NOT USED) + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 1, // 1 toggle role + ToggleAssign, // Assign Toggles + ToggleGet, // Get Toggles + ToggleOff, // Process toggle off events + ToggleOn, // Process toggle on events + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, // No dial roles + 0, // Assign Dials (NOT USED) + 0, // Get Dials (NOT USED) + 0, // Process dial move events (NOT USED) + }, + #endif +}; + +GHandle gwinGButtonCreate(GDisplay *g, GButtonObject *gw, const GWidgetInit *pInit) { + if (!(gw = (GButtonObject *)_gwidgetCreate(g, &gw->w, pInit, &buttonVMT))) + return 0; + + #if GINPUT_NEED_TOGGLE + gw->toggle = GWIDGET_NO_INSTANCE; + #endif + gwinSetVisible((GHandle)gw, pInit->g.show); + return (GHandle)gw; +} + +bool_t gwinButtonIsPressed(GHandle gh) { + if (gh->vmt != (gwinVMT *)&buttonVMT) + return FALSE; + + return (gh->flags & GBUTTON_FLG_PRESSED) ? TRUE : FALSE; +} + +/*---------------------------------------------------------- + * Custom Draw Routines + *----------------------------------------------------------*/ + +static const GColorSet *getDrawColors(GWidgetObject *gw) { + if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled; + if ((gw->g.flags & GBUTTON_FLG_PRESSED)) return &gw->pstyle->pressed; + return &gw->pstyle->enabled; +} + +#if GWIN_FLAT_STYLING + void gwinButtonDraw_Normal(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; + pcol = getDrawColors(gw); + + gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); + gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); + } +#else + void gwinButtonDraw_Normal(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + fixed alpha; + fixed dalpha; + coord_t i; + color_t tcol, bcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; + pcol = getDrawColors(gw); + + /* Fill the box blended from variants of the fill color */ + tcol = gdispBlendColor(White, pcol->fill, TOP_FADE); + bcol = gdispBlendColor(Black, pcol->fill, BOTTOM_FADE); + dalpha = FIXED(255)/gw->g.height; + for(alpha = 0, i = 0; i < gw->g.height; i++, alpha += dalpha) + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+i, gw->g.x+gw->g.width-2, gw->g.y+i, gdispBlendColor(bcol, tcol, NONFIXED(alpha))); + + gdispGDrawStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, justifyCenter); + gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); + } +#endif + +#if GDISP_NEED_ARC + void gwinButtonDraw_Rounded(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; + pcol = getDrawColors(gw); + + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); + if (gw->g.width >= 2*RND_CNR_SIZE+10) { + gdispGFillRoundedBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, RND_CNR_SIZE-1, pcol->fill); + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+RND_CNR_SIZE, gw->g.width-2, gw->g.height-(2*RND_CNR_SIZE), gw->text, gw->g.font, pcol->text, justifyCenter); + gdispGDrawRoundedBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, RND_CNR_SIZE, pcol->edge); + } else { + gdispGFillStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); + } + } +#endif + +#if GDISP_NEED_ELLIPSE + void gwinButtonDraw_Ellipse(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; + pcol = getDrawColors(gw); + + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); + gdispGFillEllipse(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width/2-1, gw->g.height/2-1, pcol->fill); + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + gdispGDrawEllipse(gw->g.display, gw->g.x, gw->g.y, gw->g.width/2, gw->g.height/2, pcol->edge); + } +#endif + +#if GDISP_NEED_CONVEX_POLYGON + void gwinButtonDraw_ArrowUp(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + (void) param; + point arw[7]; + + if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; + pcol = getDrawColors(gw); + + arw[0].x = gw->g.width/2; arw[0].y = 0; + arw[1].x = gw->g.width-1; arw[1].y = gw->g.height/ARROWHEAD_DIVIDER; + arw[2].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[2].y = gw->g.height/ARROWHEAD_DIVIDER; + arw[3].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[3].y = gw->g.height-1; + arw[4].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[4].y = gw->g.height-1; + arw[5].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[5].y = gw->g.height/ARROWHEAD_DIVIDER; + arw[6].x = 0; arw[6].y = gw->g.height/ARROWHEAD_DIVIDER; + + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); + gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); + gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + } + + void gwinButtonDraw_ArrowDown(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + (void) param; + point arw[7]; + + if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; + pcol = getDrawColors(gw); + + arw[0].x = gw->g.width/2; arw[0].y = gw->g.height-1; + arw[1].x = gw->g.width-1; arw[1].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; + arw[2].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[2].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; + arw[3].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[3].y = 0; + arw[4].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[4].y = 0; + arw[5].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[5].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; + arw[6].x = 0; arw[6].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; + + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); + gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); + gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + } + + void gwinButtonDraw_ArrowLeft(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + (void) param; + point arw[7]; + + if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; + pcol = getDrawColors(gw); + + arw[0].x = 0; arw[0].y = gw->g.height/2; + arw[1].x = gw->g.width/ARROWHEAD_DIVIDER; arw[1].y = 0; + arw[2].x = gw->g.width/ARROWHEAD_DIVIDER; arw[2].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; + arw[3].x = gw->g.width-1; arw[3].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; + arw[4].x = gw->g.width-1; arw[4].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; + arw[5].x = gw->g.width/ARROWHEAD_DIVIDER; arw[5].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; + arw[6].x = gw->g.width/ARROWHEAD_DIVIDER; arw[6].y = gw->g.height-1; + + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); + gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); + gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + } + + void gwinButtonDraw_ArrowRight(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + (void) param; + point arw[7]; + + if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; + pcol = getDrawColors(gw); + + arw[0].x = gw->g.width-1; arw[0].y = gw->g.height/2; + arw[1].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[1].y = 0; + arw[2].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[2].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; + arw[3].x = 0; arw[3].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; + arw[4].x = 0; arw[4].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; + arw[5].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[5].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; + arw[6].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[6].y = gw->g.height-1; + + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); + gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); + gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + } +#endif + +#if GDISP_NEED_IMAGE || defined(__DOXYGEN__) + void gwinButtonDraw_Image(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + coord_t sy; + + if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; + pcol = getDrawColors(gw); + + if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) { + sy = 2 * gw->g.height; + } else if ((gw->g.flags & GBUTTON_FLG_PRESSED)) { + sy = gw->g.height; + } else { + sy = 0; + } + + gdispGImageDraw(gw->g.display, (gdispImage *)param, gw->g.x, gw->g.y, gw->g.width, gw->g.height, 0, sy); + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + } +#endif + +#endif /* GFX_USE_GWIN && GWIN_NEED_BUTTON */ diff --git a/src/gwin/gwin_button.h b/src/gwin/gwin_button.h new file mode 100644 index 00000000..282ed828 --- /dev/null +++ b/src/gwin/gwin_button.h @@ -0,0 +1,135 @@ +/* + * 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_button.h + * @brief GWIN Graphic window subsystem header file. + * + * @defgroup Button Button + * @ingroup Widgets + * + * @details GWIN allows it to easily create buttons with different styles + * and check for different meta states such as: PRESSED, CLICKED, + * RELEASED etc. + * + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_BUTTON must be set to TRUE in your gfxconf.h + * @{ + */ + +#ifndef _GWIN_BUTTON_H +#define _GWIN_BUTTON_H + +/* This file is included within "src/gwin/gwin_widget.h" */ + +/** + * @brief The Event Type for a Button Event + */ +#define GEVENT_GWIN_BUTTON (GEVENT_GWIN_CTRL_FIRST+0) + +/** + * @brief A Button Event + * @note There are currently no GEventGWinButton listening flags - use 0 as the flags to @p gwinAttachListener() + */ +typedef GEventGWin GEventGWinButton; + +/** + * @brief The button widget structure + * @note Do not use the members directly - treat it as a black-box. + */ +typedef struct GButtonObject { + GWidgetObject w; + #if GINPUT_NEED_TOGGLE + uint16_t toggle; + #endif +} GButtonObject; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a button widget. + * @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] gb The GButtonObject structure to initialise. If this is NULL the structure is dynamically allocated. + * @param[in] pInit The initialisation parameters + * + * @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 button remembers its normal drawing state. If there is a window manager then it is automatically + * redrawn if the window is moved or its visibility state is changed. + * @note A button supports mouse and a toggle input. + * @note When assigning a toggle, only one toggle is supported. If you try to assign more than one toggle it will + * forget the previous toggle. When assigning a toggle the role parameter must be 0. + * + * @api + */ +GHandle gwinGButtonCreate(GDisplay *g, GButtonObject *gb, const GWidgetInit *pInit); +#define gwinButtonCreate(gb, pInit) gwinGButtonCreate(GDISP, gb, pInit) + +/** + * @brief Is the button current pressed + * @return TRUE if the button is pressed + * + * @param[in] gh The window handle (must be a button widget) + * + * @api + */ +bool_t gwinButtonIsPressed(GHandle gh); + +/** + * @brief Some custom button drawing routines + * @details These function may be passed to @p gwinSetCustomDraw() to get different button drawing styles + * + * @param[in] gw The widget object (in this case a button) + * @param[in] param A parameter passed in from the user + * + * @note In your custom button drawing function you may optionally call these + * standard functions and then draw your extra details on top. + * @note The standard functions below ignore the param parameter except for @p gwinButtonDraw_Image(). + * @note The image custom draw function @p gwinButtonDraw_Image() uses param to pass in the gdispImage pointer. + * The image must be already opened before calling @p gwinSetCustomDraw(). The image should be 3 + * times the height of the button. The button image is repeated 3 times vertically, the first (top) for + * the "up" image, the 2nd for the "down" image, and the third (bottom) image for the disabled state. If + * the disabled state is never going to be used then the image can be just 2 times the button height. + * No checking is done to compare the size of the button to the size of the image. + * Note text is drawn on top of the image. + * @note These custom drawing routines don't have to worry about setting clipping as the framework + * sets clipping to the object window prior to calling these routines. + * + * @api + * @{ + */ +void gwinButtonDraw_Normal(GWidgetObject *gw, void *param); // @< A standard button +#if GDISP_NEED_ARC || defined(__DOXYGEN__) + void gwinButtonDraw_Rounded(GWidgetObject *gw, void *param); // @< A rounded rectangle button +#endif +#if GDISP_NEED_ELLIPSE || defined(__DOXYGEN__) + void gwinButtonDraw_Ellipse(GWidgetObject *gw, void *param); // @< A circular button +#endif +#if GDISP_NEED_CONVEX_POLYGON || defined(__DOXYGEN__) + void gwinButtonDraw_ArrowUp(GWidgetObject *gw, void *param); // @< An up arrow button + void gwinButtonDraw_ArrowDown(GWidgetObject *gw, void *param); // @< A down arrow button + void gwinButtonDraw_ArrowLeft(GWidgetObject *gw, void *param); // @< A left arrow button + void gwinButtonDraw_ArrowRight(GWidgetObject *gw, void *param); // @< A right arrow button +#endif +#if GDISP_NEED_IMAGE || defined(__DOXYGEN__) + void gwinButtonDraw_Image(GWidgetObject *gw, void *param); // @< An image button - see the notes above on the param. +#endif +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GWIN_BUTTON_H */ +/** @} */ + diff --git a/src/gwin/gwin_checkbox.c b/src/gwin/gwin_checkbox.c new file mode 100644 index 00000000..dec085c3 --- /dev/null +++ b/src/gwin/gwin_checkbox.c @@ -0,0 +1,195 @@ +/* + * 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_checkbox.c + * @brief GWIN sub-system button code + */ + +#include "gfx.h" + +#if (GFX_USE_GWIN && GWIN_NEED_CHECKBOX) || defined(__DOXYGEN__) + +#include "gwin_class.h" + +// Our checked state +#define GCHECKBOX_FLG_CHECKED (GWIN_FIRST_CONTROL_FLAG<<0) + +// Send the checkbox event +static void SendCheckboxEvent(GWidgetObject *gw) { + GSourceListener * psl; + GEvent * pe; + #define pce ((GEventGWinCheckbox *)pe) + + // Trigger a GWIN Checkbox Event + psl = 0; + while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { + if (!(pe = geventGetEventBuffer(psl))) + continue; + pce->type = GEVENT_GWIN_CHECKBOX; + pce->gwin = &gw->g; + pce->isChecked = (gw->g.flags & GCHECKBOX_FLG_CHECKED) ? TRUE : FALSE; + #if GWIN_WIDGET_TAGS + pce->tag = gw->tag; + #endif + geventSendEvent(psl); + } + + #undef pce +} + +#if GINPUT_NEED_MOUSE + static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { + (void) x; (void) y; + gw->g.flags ^= GCHECKBOX_FLG_CHECKED; + _gwinUpdate((GHandle)gw); + SendCheckboxEvent(gw); + } +#endif + +#if GINPUT_NEED_TOGGLE + static void ToggleOn(GWidgetObject *gw, uint16_t role) { + (void) role; + gw->g.flags ^= GCHECKBOX_FLG_CHECKED; + _gwinUpdate((GHandle)gw); + SendCheckboxEvent(gw); + } + + static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { + (void) role; + ((GCheckboxObject *)gw)->toggle = instance; + } + + static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { + (void) role; + return ((GCheckboxObject *)gw)->toggle; + } +#endif + +// The checkbox VMT table +static const gwidgetVMT checkboxVMT = { + { + "Checkbox", // The classname + sizeof(GCheckboxObject),// The object size + _gwidgetDestroy, // The destroy routine + _gwidgetRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinCheckboxDraw_CheckOnLeft, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + MouseDown, // Process mouse down events + 0, // Process mouse up events (NOT USED) + 0, // Process mouse move events (NOT USED) + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 1, // 1 toggle role + ToggleAssign, // Assign Toggles + ToggleGet, // Get Toggles + 0, // Process toggle off events (NOT USED) + ToggleOn, // Process toggle on events + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, // No dial roles + 0, // Assign Dials (NOT USED) + 0, // Get Dials (NOT USED) + 0, // Process dial move events (NOT USED) + }, + #endif +}; + +GHandle gwinGCheckboxCreate(GDisplay *g, GCheckboxObject *gb, const GWidgetInit *pInit) { + if (!(gb = (GCheckboxObject *)_gwidgetCreate(g, &gb->w, pInit, &checkboxVMT))) + return 0; + + #if GINPUT_NEED_TOGGLE + gb->toggle = GWIDGET_NO_INSTANCE; + #endif + gwinSetVisible((GHandle)gb, pInit->g.show); + return (GHandle)gb; +} + +void gwinCheckboxCheck(GHandle gh, bool_t isChecked) { + if (gh->vmt != (gwinVMT *)&checkboxVMT) + return; + + if (isChecked) { + if ((gh->flags & GCHECKBOX_FLG_CHECKED)) return; + gh->flags |= GCHECKBOX_FLG_CHECKED; + } else { + if (!(gh->flags & GCHECKBOX_FLG_CHECKED)) return; + gh->flags &= ~GCHECKBOX_FLG_CHECKED; + } + _gwinUpdate(gh); + SendCheckboxEvent((GWidgetObject *)gh); +} + +bool_t gwinCheckboxIsChecked(GHandle gh) { + if (gh->vmt != (gwinVMT *)&checkboxVMT) + return FALSE; + + return (gh->flags & GCHECKBOX_FLG_CHECKED) ? TRUE : FALSE; +} + +/*---------------------------------------------------------- + * Custom Draw Routines + *----------------------------------------------------------*/ + +static const GColorSet *getDrawColors(GWidgetObject *gw) { + if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled; + if ((gw->g.flags & GCHECKBOX_FLG_CHECKED)) return &gw->pstyle->pressed; + return &gw->pstyle->enabled; +} + +void gwinCheckboxDraw_CheckOnLeft(GWidgetObject *gw, void *param) { + #define gcw ((GCheckboxObject *)gw) + coord_t ld, df; + const GColorSet * pcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&checkboxVMT) return; + pcol = getDrawColors(gw); + + ld = gw->g.width < gw->g.height ? gw->g.width : gw->g.height; + gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+1, ld, ld-2, gw->pstyle->background); + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, ld, ld, pcol->edge); + + df = ld < 4 ? 1 : 2; + if (gw->g.flags & GCHECKBOX_FLG_CHECKED) + gdispGFillArea(gw->g.display, gw->g.x+df, gw->g.y+df, ld-2*df, ld-2*df, pcol->fill); + + gdispGFillStringBox(gw->g.display, gw->g.x+ld+1, gw->g.y, gw->g.width-ld-1, gw->g.height, gw->text, gw->g.font, pcol->text, gw->pstyle->background, justifyLeft); + #undef gcw +} + +void gwinCheckboxDraw_CheckOnRight(GWidgetObject *gw, void *param) { + #define gcw ((GCheckboxObject *)gw) + coord_t ep, ld, df; + const GColorSet * pcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&checkboxVMT) return; + pcol = getDrawColors(gw); + + ld = gw->g.width < gw->g.height ? gw->g.width : gw->g.height; + ep = gw->g.width-ld-1; + gdispGFillArea(gw->g.display, gw->g.x+ep-1, gw->g.y+1, ld, ld-2, gw->pstyle->background); + gdispGDrawBox(gw->g.display, gw->g.x+ep, gw->g.y, ld, ld, pcol->edge); + + df = ld < 4 ? 1 : 2; + if (gw->g.flags & GCHECKBOX_FLG_CHECKED) + gdispGFillArea(gw->g.display, gw->g.x+ep+df, gw->g.y+df, ld-2*df, ld-2*df, pcol->fill); + + gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, ep-1, gw->g.height, gw->text, gw->g.font, pcol->text, gw->pstyle->background, justifyRight); + #undef gcw +} + +#endif /* (GFX_USE_GWIN && GWIN_NEED_CHECKBOX) */ diff --git a/src/gwin/gwin_checkbox.h b/src/gwin/gwin_checkbox.h new file mode 100644 index 00000000..3a67d487 --- /dev/null +++ b/src/gwin/gwin_checkbox.h @@ -0,0 +1,127 @@ +/* + * 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_checkbox.h + * @brief GWIN Graphic window subsystem header file. + * + * @defgroup Checkbox Checkbox + * @ingroup Widgets + * + * @details GWIN allows it to easily create a group of checkbox buttons. + * + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_CHECKBOX must be set to TRUE in your gfxconf.h + * @{ + */ + +#ifndef _GWIN_CHECKBOX_H +#define _GWIN_CHECKBOX_H + +/* This file is included within "src/gwin/gwin_widget.h" */ + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define GEVENT_GWIN_CHECKBOX (GEVENT_GWIN_CTRL_FIRST+2) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +typedef struct GEventGWinCheckbox { + GEventType type; // The type of this event (GEVENT_GWIN_CHECKBOX) + GHandle gwin; // The checkbox that has been depressed (actually triggered on release) + #if GWIN_WIDGET_TAGS + WidgetTag tag; // The checkbox tag + #endif + bool_t isChecked; // Is the checkbox currently checked or unchecked? +} GEventGWinCheckbox; + +/* A Checkbox window */ +typedef struct GCheckboxObject { + GWidgetObject w; + #if GINPUT_NEED_TOGGLE + uint16_t toggle; + #endif +} GCheckboxObject; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a checkbox 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] gb The GCheckboxObject structure to initialise. If this is NULL, the structure is dynamically allocated. + * @param[in] pInit The initialization parameters to use + * + * @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 checkbox remembers its normal drawing state. If there is a window manager then it is automatically + * redrawn if the window is moved or its visibility state is changed. + * @note A checkbox supports mouse and a toggle input. + * @note When assigning a toggle, only one toggle is supported. If you try to assign more than one toggle it will + * forget the previous toggle. When assigning a toggle the role parameter must be 0. + * + * @api + */ +GHandle gwinGCheckboxCreate(GDisplay *g, GCheckboxObject *gb, const GWidgetInit *pInit); +#define gwinCheckboxCreate(gb, pInit) gwinGCheckboxCreate(GDISP, gb, pInit) + +/** + * @brief Set the state of a checkbox + * + * @param[in] gh The window handle (must be a checkbox window) + * @param[in] isChecked TRUE to set the check, FALSE to uncheck. + * + * @api + */ +void gwinCheckboxCheck(GHandle gh, bool_t isChecked); + +/** + * @brief Get the state of a checkbox + * @return TRUE if the checkbox is currently checked + * + * @param[in] gh The window handle (must be a checkbox window) + * + * @api + */ +bool_t gwinCheckboxIsChecked(GHandle gh); + +/** + * @brief Some custom checkbox drawing routines + * @details These function may be passed to @p gwinSetCustomDraw() to get different checkbox drawing styles + * + * @param[in] gw The widget (which must be a checkbox) + * @param[in] param A parameter passed in from the user + * + * @note In your custom checkbox drawing function you may optionally call this + * standard functions and then draw your extra details on top. + * @note The standard functions below ignore the param parameter. + * @note These custom drawing routines don't have to worry about setting clipping as the framework + * sets clipping to the object window prior to calling these routines. + * + * @api + * @{ + */ +void gwinCheckboxDraw_CheckOnLeft(GWidgetObject *gw, void *param); +void gwinCheckboxDraw_CheckOnRight(GWidgetObject *gw, void *param); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GWIN_CHECKBOX_H */ +/** @} */ + diff --git a/src/gwin/gwin_class.h b/src/gwin/gwin_class.h new file mode 100644 index 00000000..4dde3624 --- /dev/null +++ b/src/gwin/gwin_class.h @@ -0,0 +1,363 @@ +/* + * 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_class.h + * @brief GWIN Graphic window subsystem header file. + * + * @defgroup Internal Internal + * @ingroup GWIN + * + * @note These definitions are normally not used by an application program. They are useful + * only if you want to create your own custom GWIN window or widget. + * @note To access these definitions you must include "src/gwin/gwin_class.h" in your source file. + * + * @{ + */ +#ifndef _CLASS_GWIN_H +#define _CLASS_GWIN_H + +#if GFX_USE_GWIN || defined(__DOXYGEN__) + +#if defined(__KEIL__) || defined(__C51__) + #pragma anon_unions +#endif + +/** + * @brief The predefined flags for a Window + * @{ + */ +#define GWIN_FIRST_CONTROL_FLAG 0x00000001 // @< 8 bits free for the control to use +#define GWIN_LAST_CONTROL_FLAG 0x00000080 // @< 8 bits free for the control to use +#define GWIN_FLG_VISIBLE 0x00000100 // @< The window is "visible" +#define GWIN_FLG_SYSVISIBLE 0x00000200 // @< The window is visible after parents are tested +#define GWIN_FLG_ENABLED 0x00000400 // @< The window is "enabled" +#define GWIN_FLG_SYSENABLED 0x00000800 // @< The window is enabled after parents are tested +#define GWIN_FLG_DYNAMIC 0x00001000 // @< The GWIN structure is allocated +#define GWIN_FLG_ALLOCTXT 0x00002000 // @< The text/label is allocated +#define GWIN_FLG_NEEDREDRAW 0x00004000 // @< Redraw is needed but has been delayed +#define GWIN_FLG_BGREDRAW 0x00008000 // @< On redraw, if not visible redraw the revealed under-side +#define GWIN_FLG_SUPERMASK 0x000F0000 // @< The bit mask to leave just the window superclass type +#define GWIN_FLG_WIDGET 0x00010000 // @< This is a widget +#define GWIN_FLG_CONTAINER 0x00020000 // @< This is a container +#define GWIN_FLG_MINIMIZED 0x00100000 // @< The window is minimized +#define GWIN_FLG_MAXIMIZED 0x00200000 // @< The window is maximized +#define GWIN_FLG_MOUSECAPTURE 0x00400000 // @< The window has captured the mouse +#define GWIN_FIRST_WM_FLAG 0x01000000 // @< 8 bits free for the window manager to use +#define GWIN_LAST_WM_FLAG 0x80000000 // @< 8 bits free for the window manager to use +/** @} */ + +/** + * @brief The Virtual Method Table for a GWIN window + * @{ + */ +typedef struct gwinVMT { + const char * classname; // @< The GWIN classname (mandatory) + size_t size; // @< The size of the class object + void (*Destroy) (GWindowObject *gh); // @< The GWIN destroy function (optional) + void (*Redraw) (GWindowObject *gh); // @< The GWIN redraw routine (optional) + void (*AfterClear) (GWindowObject *gh); // @< The GWIN after-clear function (optional) +} gwinVMT; +/** @} */ + +#if GWIN_NEED_WIDGET || defined(__DOXYGEN__) + + /** + * @brief An toggle/dial instance is not being used + */ + #define GWIDGET_NO_INSTANCE ((uint16_t)-1) + + /** + * @brief The source handle that widgets use when sending events + */ + #define GWIDGET_SOURCE ((GSourceHandle)(void *)_gwidgetCreate) + + /** + * @brief The Virtual Method Table for a widget + * @note A widget must have a destroy function. Either use @p _gwidgetDestroy() or use your own function + * which internally calls @p _gwidgetDestroy(). + * @note A widget must have a redraw function. Use @p _gwidgetRedraw(). + * @note If toggleroles != 0, ToggleAssign(), ToggleGet() and one or both of ToggleOff() and ToggleOn() must be specified. + * @note If dialroles != 0, DialAssign(), DialGet() and DialMove() must be specified. + * @{ + */ + typedef struct gwidgetVMT { + struct gwinVMT g; // @< This is still a GWIN + void (*DefaultDraw) (GWidgetObject *gw, void *param); // @< The default drawing routine (mandatory) + #if GINPUT_NEED_MOUSE + struct { + void (*MouseDown) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse down events (optional) + void (*MouseUp) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse up events (optional) + void (*MouseMove) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse move events (optional) + }; + #endif + #if GINPUT_NEED_TOGGLE + struct { + uint16_t toggleroles; // @< The roles supported for toggles (0->toggleroles-1) + void (*ToggleAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); // @< Assign a toggle to a role (optional) + uint16_t (*ToggleGet) (GWidgetObject *gw, uint16_t role); // @< Return the instance for a particular role (optional) + void (*ToggleOff) (GWidgetObject *gw, uint16_t role); // @< Process toggle off events (optional) + void (*ToggleOn) (GWidgetObject *gw, uint16_t role); // @< Process toggle on events (optional) + }; + #endif + #if GINPUT_NEED_DIAL + struct { + uint16_t dialroles; // @< The roles supported for dials (0->dialroles-1) + void (*DialAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); // @< Test the role and save the dial instance handle (optional) + uint16_t (*DialGet) (GWidgetObject *gw, uint16_t role); // @< Return the instance for a particular role (optional) + void (*DialMove) (GWidgetObject *gw, uint16_t role, uint16_t value, uint16_t max); // @< Process dial move events (optional) + }; + #endif + } gwidgetVMT; + /** @} */ +#endif + +#if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) + + /** + * @brief The Virtual Method Table for a container + * @note A container must have a destroy function. Either use @p _gcontainerDestroy() or use your own function + * which internally calls @p _gcontainerDestroy(). + * @note A container must have a gwin redraw function. Use @p _containerRedraw(). + * @note If toggleroles != 0, ToggleAssign(), ToggleGet() and one or both of ToggleOff() and ToggleOn() must be specified. + * @note If dialroles != 0, DialAssign(), DialGet() and DialMove() must be specified. + * @{ + */ + typedef struct gcontainerVMT { + gwidgetVMT gw; + coord_t (*LeftBorder) (GHandle gh); // @< The size of the left border (mandatory) + coord_t (*TopBorder) (GHandle gh); // @< The size of the top border (mandatory) + coord_t (*RightBorder) (GHandle gh); // @< The size of the right border (mandatory) + coord_t (*BottomBorder) (GHandle gh); // @< The size of the bottom border (mandatory) + void (*NotifyAdd) (GHandle gh, GHandle ghChild); // @< Notification that a child has been added (optional) + void (*NotifyDelete) (GHandle gh, GHandle ghChild); // @< Notification that a child has been deleted (optional) + } gcontainerVMT; + /** @} */ +#endif + +#if GWIN_NEED_WINDOWMANAGER || defined(__DOXYGEN__) + // @note There is only ever one instance of each GWindowManager type + typedef struct GWindowManager { + const struct gwmVMT *vmt; + } GWindowManager; + + /** + * @brief The Virtual Method Table for a window manager + * @{ + */ + typedef struct gwmVMT { + void (*Init) (void); // @< The window manager has just been set as the current window manager + void (*DeInit) (void); // @< The window manager has just been removed as the current window manager + bool_t (*Add) (GHandle gh, const GWindowInit *pInit); // @< A window has been added + void (*Delete) (GHandle gh); // @< A window has been deleted + void (*Redraw) (GHandle gh); // @< A window needs to be redraw (or undrawn) + void (*Size) (GHandle gh, coord_t w, coord_t h); // @< A window wants to be resized + void (*Move) (GHandle gh, coord_t x, coord_t y); // @< A window wants to be moved + void (*Raise) (GHandle gh); // @< A window wants to be on top + void (*MinMax) (GHandle gh, GWindowMinMax minmax); // @< A window wants to be minimized/maximised + } gwmVMT; + /** @} */ + + /** + * @brief The current window manager + */ + extern GWindowManager * _GWINwm; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialise (and allocate if necessary) the base GWIN object + * + * @param[in] g The GDisplay to use for this window + * @param[in] pgw The GWindowObject structure. If NULL one is allocated from the heap + * @param[in] pInit The user initialization parameters + * @param[in] vmt The virtual method table for the GWIN object + * @param[in] flags The default flags to use + * + * @return The GHandle of the created window + * + * @notapi + */ +GHandle _gwindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit, const gwinVMT *vmt, uint32_t flags); + +/** + * @brief Redraw the window after a status change. + * + * @param[in] gh The widget to redraw + * + * @note Mark a window for redraw. + * @note The window will get redrawn at some later time. + * @note This call is designed to be fast and non-blocking + * + * @notapi + */ +void _gwinUpdate(GHandle gh); + +/** + * @brief How to flush the redraws + * @notes REDRAW_WAIT - Wait for a drawing session to be available + * @notes REDRAW_NOWAIT - Do nothing if the drawing session is not available + * @note REDRAW_INSESSION - We are already in a drawing session + */ +typedef enum GRedrawMethod { REDRAW_WAIT, REDRAW_NOWAIT, REDRAW_INSESSION } GRedrawMethod; + +/** + * @brief Flush any pending redraws in the system. + * + * @param[in] how Do we wait for the lock? + * + * @note This call will attempt to flush any pending redraws + * in the system. The doWait parameter tells this call + * how to handle someone already holding the drawing lock. + * If doWait is TRUE it waits to obtain the lock. If FALSE + * and the drawing lock is free then the redraw is done + * immediately. If the drawing lock was taken it will postpone the flush + * on the basis that someone else will do it for us later. + * + * @notapi + */ +void _gwinFlushRedraws(GRedrawMethod how); + +/** + * @brief Obtain a drawing session + * @return TRUE if the drawing session was obtained, FALSE if the window is not visible + * + * @param[in] gh The window + * + * @note This function blocks until a drawing session is available if the window is visible + */ +bool_t _gwinDrawStart(GHandle gh); + +/** + * @brief Release a drawing session + * + * @param[in] gh The window + */ +void _gwinDrawEnd(GHandle gh); + +/** + * @brief Destroy a window. + * + * @param[in] gh The window + * @param[in] how Do we wait for the lock? + * + * @note This call will delete the window. If called without the + * drawing lock 'how' must be REDRAW_WAIT. If called with the drawing + * lock 'how' must be REDRAW_INSESSION. + * + * @notapi + */ +void _gwinDestroy(GHandle gh, GRedrawMethod how); + +/** + * @brief Add a window to the window manager and set its position and size + * @return TRUE if successful + * + * @param[in] gh The window + * @param[in] pInit The window init structure + */ +bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit); + +#if GWIN_NEED_WIDGET || defined(__DOXYGEN__) + /** + * @brief Initialise (and allocate if necessary) the base Widget object + * + * @param[in] g The GDisplay to display this window on + * @param[in] pgw The GWidgetObject structure. If NULL one is allocated from the heap + * @param[in] pInit The user initialization parameters + * @param[in] vmt The virtual method table for the Widget object + * + * @return The GHandle of the created widget + * + * @notapi + */ + GHandle _gwidgetCreate(GDisplay *g, GWidgetObject *pgw, const GWidgetInit *pInit, const gwidgetVMT *vmt); + + /** + * @brief Destroy the Widget object + * + * @param[in] gh The widget to destroy + * + * @notapi + */ + void _gwidgetDestroy(GHandle gh); + + /** + * @brief Redraw the Widget object (VMT method only) + * + * @param[in] gh The widget to redraw + * + * @note Do not use this routine to update a widget after a status change. + * Use @p _gwinUpdate() instead. This routine should only be used in the + * VMT. + * + * @notapi + */ + void _gwidgetRedraw(GHandle gh); + + /** + * @brief Send a standard GWIN event. + * + * @param[in] gh The window + * @param[in] type The event type + * + * @note No consideration is given to recording EVENT LOST statuses. + * + * @notapi + */ + void _gwinSendEvent(GHandle gh, GEventType type); +#endif + +#if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) + /** + * @brief Initialise (and allocate if necessary) the base Container object + * + * @param[in] g The GDisplay to display this window on + * @param[in] pgw The GContainerObject structure. If NULL one is allocated from the heap + * @param[in] pInit The user initialization parameters + * @param[in] vmt The virtual method table for the Container object + * + * @return The GHandle of the created widget + * + * @notapi + */ + GHandle _gcontainerCreate(GDisplay *g, GContainerObject *pgw, const GWidgetInit *pInit, const gcontainerVMT *vmt); + + /** + * @brief Destroy the Container object + * + * @param[in] gh The container to destroy + * + * @notapi + */ + void _gcontainerDestroy(GHandle gh); + + /** + * @brief Redraw the Container object (VMT method only) + * + * @param[in] gh The container to redraw + * + * @note Do not use this routine to update a container after a status change. + * Use @p _gwinUpdate() instead. This routine should only be used in the + * VMT. + * + * @notapi + */ + #define _gcontainerRedraw _gwidgetRedraw +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GWIN */ + +#endif /* _CLASS_GWIN_H */ +/** @} */ diff --git a/src/gwin/gwin_console.c b/src/gwin/gwin_console.c new file mode 100644 index 00000000..4c17034d --- /dev/null +++ b/src/gwin/gwin_console.c @@ -0,0 +1,809 @@ +/* + * 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_console.c + * @brief GWIN sub-system console code. + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_CONSOLE + +#include + +#include "gwin_class.h" + +#define GWIN_CONSOLE_USE_CLEAR_LINES TRUE // Clear each line before using it +#define GWIN_CONSOLE_USE_FILLED_CHARS FALSE // Use filled characters instead of drawn characters +#define GWIN_CONSOLE_BUFFER_SCROLLING TRUE // Use the history buffer to scroll when it is available + +// Our control flags +#define GCONSOLE_FLG_NOSTORE (GWIN_FIRST_CONTROL_FLAG<<0) +#define GCONSOLE_FLG_OVERRUN (GWIN_FIRST_CONTROL_FLAG<<1) + +// Meaning of our attribute bits. +#define ESC_REDBIT 0x01 +#define ESC_GREENBIT 0x02 +#define ESC_BLUEBIT 0x04 +#define ESC_USECOLOR 0x08 +#define ESC_UNDERLINE 0x10 +#define ESC_BOLD 0x20 + +/* + * Stream interface implementation. The interface is write only + */ + +#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM + #define Stream2GWindow(ip) ((GHandle)(((char *)(ip)) - (size_t)(&(((GConsoleObject *)0)->stream)))) + + static size_t GWinStreamWrite(void *ip, const uint8_t *bp, size_t n) { gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; } + static size_t GWinStreamRead(void *ip, uint8_t *bp, size_t n) { (void)ip; (void)bp; (void)n; return 0; } + static msg_t GWinStreamPut(void *ip, uint8_t b) { gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; } + static msg_t GWinStreamGet(void *ip) {(void)ip; return RDY_OK; } + static msg_t GWinStreamPutTimed(void *ip, uint8_t b, systime_t time) { (void)time; gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; } + static msg_t GWinStreamGetTimed(void *ip, systime_t timeout) { (void)ip; (void)timeout; return RDY_OK; } + static size_t GWinStreamWriteTimed(void *ip, const uint8_t *bp, size_t n, systime_t time) { (void)time; gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; } + static size_t GWinStreamReadTimed(void *ip, uint8_t *bp, size_t n, systime_t time) { (void)ip; (void)bp; (void)n; (void)time; return 0; } + + struct GConsoleWindowVMT_t { + _base_asynchronous_channel_methods + }; + + static const struct GConsoleWindowVMT_t GWindowConsoleVMT = { + GWinStreamWrite, + GWinStreamRead, + GWinStreamPut, + GWinStreamGet, + GWinStreamPutTimed, + GWinStreamGetTimed, + GWinStreamWriteTimed, + GWinStreamReadTimed + }; +#endif + +#if GWIN_CONSOLE_ESCSEQ + // Convert escape sequences to attributes + static bool_t ESCtoAttr(char c, uint8_t *pattr) { + uint8_t attr; + + attr = pattr[0]; + switch(c) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + attr &= ~(ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT); + attr |= (c - '0') | ESC_USECOLOR; + break; + case 'C': + attr &= ~(ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT|ESC_USECOLOR); + break; + case 'u': + attr |= ESC_UNDERLINE; + break; + case 'U': + attr &= ~ESC_UNDERLINE; + break; + case 'b': + attr |= ESC_BOLD; + break; + case 'B': + attr &= ~ESC_BOLD; + break; + default: + return FALSE; + } + if (attr == pattr[0]) + return FALSE; + pattr[0] = attr; + return TRUE; + } + + static color_t ESCPrintColor(GConsoleObject *gcw) { + switch(gcw->currattr & (ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT|ESC_USECOLOR)) { + case (ESC_USECOLOR): + return Black; + case (ESC_USECOLOR|ESC_REDBIT): + return Red; + case (ESC_USECOLOR|ESC_GREENBIT): + return Green; + case (ESC_USECOLOR|ESC_REDBIT|ESC_GREENBIT): + return Yellow; + case (ESC_USECOLOR|ESC_BLUEBIT): + return Blue; + case (ESC_USECOLOR|ESC_REDBIT|ESC_BLUEBIT): + return Magenta; + case (ESC_USECOLOR|ESC_GREENBIT|ESC_BLUEBIT): + return Cyan; + case (ESC_USECOLOR|ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT): + return White; + default: + return gcw->g.color; + } + } +#else + #define ESCPrintColor(gcw) ((gcw)->g.color) +#endif + +#if GWIN_CONSOLE_USE_HISTORY + static void HistoryDestroy(GWindowObject *gh) { + #define gcw ((GConsoleObject *)gh) + + // Deallocate the history buffer if required. + if (gcw->buffer) { + gfxFree(gcw->buffer); + gcw->buffer = 0; + } + + #undef gcw + } + + static void HistoryRedraw(GWindowObject *gh) { + #define gcw ((GConsoleObject *)gh) + + // No redrawing if there is no history + if (!gcw->buffer) + return; + + // We are printing the buffer - don't store it again + gh->flags |= GCONSOLE_FLG_NOSTORE; + + #if !GWIN_CONSOLE_USE_CLEAR_LINES + // Clear the screen + gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); + #endif + + // Reset the cursor + gcw->cx = 0; + gcw->cy = 0; + + // Reset the current attributes + #if GWIN_CONSOLE_ESCSEQ + gcw->currattr = gcw->startattr; + #endif + + // Print the buffer + gwinPutCharArray(gh, gcw->buffer, gcw->bufpos); + + #if GWIN_CONSOLE_USE_CLEAR_LINES + // Clear the remaining space + { + coord_t y; + + y = gcw->cy; + if (gcw->cx) + y += gdispGetFontMetric(gh->font, fontHeight); + if (y < gh->height) + gdispGFillArea(gh->display, gh->x, gh->y+y, gh->width, gh->height-y, gh->bgcolor); + } + #endif + + // Turn back on storing of buffer contents + gh->flags &= ~GCONSOLE_FLG_NOSTORE; + + #undef gcw + } + + /** + * Put a character into our history buffer + */ + static void putCharInBuffer(GConsoleObject *gcw, char c) { + // Only store if we need to + if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE)) + return; + + // Do we have enough space in the buffer + if (gcw->bufpos >= gcw->bufsize) { + char *p, *ep; + size_t dp; + + /** + * This should never really happen except if the user has changed the window + * size without turning off and then on the buffer. Even then it is unlikely + * because of our conservative allocation strategy. + * If it really is needed we scroll one line to make some space. We also mark + * it is an overrun so that if asked to really scroll later we know we already have. + * Note we only use one bit to indicate an overrun, so an overrun of more + * than one line will lead to some interesting scrolling and refreshing + * effects. + */ + + // Remove one line from the start + ep = gcw->buffer+gcw->bufpos; + for(p = gcw->buffer; p < ep && *p != '\n'; p++) { + #if GWIN_CONSOLE_ESCSEQ + if (*p == 27) + ESCtoAttr(p[1], &gcw->startattr); + #endif + } + + // Was there a newline? + if (*p != '\n') + p = gcw->buffer; // Oops - no newline, just delete one char + else + gcw->g.flags |= GCONSOLE_FLG_OVERRUN; // Mark the overrun + + // Delete the data + dp = ++p - gcw->buffer; // Calculate the amount to to be removed + gcw->bufpos -= dp; // Calculate the new size + if (gcw->bufpos) + memcpy(gcw->buffer, p, gcw->bufpos); // Move the rest of the data + } + + // Save the character + gcw->buffer[gcw->bufpos++] = c; + } + + /** + * Scroll the history buffer by one line + */ + static void scrollBuffer(GConsoleObject *gcw) { + char *p, *ep; + size_t dp; + + // Only scroll if we need to + if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE)) + return; + + // If a buffer overrun has been marked don't scroll as we have already + if ((gcw->g.flags & GCONSOLE_FLG_OVERRUN)) { + gcw->g.flags &= ~GCONSOLE_FLG_OVERRUN; + return; + } + + // Remove one line from the start + ep = gcw->buffer+gcw->bufpos; + for(p = gcw->buffer; p < ep && *p != '\n'; p++) { + #if GWIN_CONSOLE_ESCSEQ + if (*p == 27) + ESCtoAttr(p[1], &gcw->startattr); + #endif + } + + // Was there a newline, if not delete everything. + if (*p != '\n') { + gcw->bufpos = 0; + return; + } + + // Delete the data + dp = ++p - gcw->buffer; // Calculate the amount to to be removed + gcw->bufpos -= dp; // Calculate the new size + if (gcw->bufpos) + memcpy(gcw->buffer, p, gcw->bufpos); // Move the rest of the data + } + + /** + * Clear the history buffer + */ + static void clearBuffer(GConsoleObject *gcw) { + + // Only clear if we need to + if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE)) + return; + + gcw->bufpos = 0; + } + +#else + #define putCharInBuffer(gcw, c) + #define scrollBuffer(gcw) + #define clearBuffer(gcw) +#endif + +static void AfterClear(GWindowObject *gh) { + #define gcw ((GConsoleObject *)gh) + gcw->cx = 0; + gcw->cy = 0; + clearBuffer(gcw); + #if GWIN_CONSOLE_ESCSEQ + gcw->startattr = gcw->currattr; + #endif + #undef gcw +} + +static const gwinVMT consoleVMT = { + "Console", // The classname + sizeof(GConsoleObject), // The object size + #if GWIN_CONSOLE_USE_HISTORY + HistoryDestroy, // The destroy routine (custom) + HistoryRedraw, // The redraw routine (custom) + #else + 0, // The destroy routine + 0, // The redraw routine (default) + #endif + AfterClear, // The after-clear routine +}; + +GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *pInit) { + if (!(gc = (GConsoleObject *)_gwindowCreate(g, &gc->g, pInit, &consoleVMT, 0))) + return 0; + + #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM + gc->stream.vmt = &GWindowConsoleVMT; + #endif + + #if GWIN_CONSOLE_USE_HISTORY + gc->buffer = 0; + #if GWIN_CONSOLE_HISTORY_ATCREATE + gwinConsoleSetBuffer(&gc->g, TRUE); + #endif + #endif + + gc->cx = 0; + gc->cy = 0; + + #if GWIN_CONSOLE_ESCSEQ + gc->startattr = gc->currattr = 0; + gc->escstate = 0; + #endif + + gwinSetVisible((GHandle)gc, pInit->show); + + return (GHandle)gc; +} + +#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM + BaseSequentialStream *gwinConsoleGetStream(GHandle gh) { + if (gh->vmt != &consoleVMT) + return 0; + + return (BaseSequentialStream *)&(((GConsoleObject *)(gh))->stream); + } +#endif + +#if GWIN_CONSOLE_USE_HISTORY + bool_t gwinConsoleSetBuffer(GHandle gh, bool_t onoff) { + #define gcw ((GConsoleObject *)gh) + + if (gh->vmt != &consoleVMT) + return FALSE; + + // Do we want the buffer turned off? + if (!onoff) { + if (gcw->buffer) { + gfxFree(gcw->buffer); + gcw->buffer = 0; + } + return FALSE; + } + + // Is the buffer already on? + if (gcw->buffer) + return TRUE; + + // Get the number of characters that fit in the x direction + #if GWIN_CONSOLE_HISTORY_AVERAGING + gcw->bufsize = gh->width / ((2*gdispGetFontMetric(gh->font, fontMinWidth)+gdispGetFontMetric(gh->font, fontMaxWidth))/3); + #else + gcw->bufsize = gh->width / gdispGetFontMetric(gh->font, fontMinWidth); + #endif + gcw->bufsize++; // Allow space for a newline on each line. + + // Multiply by the number of lines + gcw->bufsize *= gh->height / gdispGetFontMetric(gh->font, fontHeight); + + // Allocate the buffer + if (!(gcw->buffer = gfxAlloc(gcw->bufsize))) + return FALSE; + + // All good! + gh->flags &= ~GCONSOLE_FLG_OVERRUN; + gcw->bufpos = 0; + return TRUE; + + #undef gcw + } +#endif + +/* + * We can get into gwinPutChar() 2 ways - + * 1. when the user calls us, and + * 2. when the redraw uses us to redraw the display. + * When called by option 2 we MUST not try to obtain a draw session + * as we already have one. + * + * We use these macro's below to make sure we do that safely + */ +#define DrawStart(gh) ((gh->flags & GCONSOLE_FLG_NOSTORE) || _gwinDrawStart(gh)) +#define DrawEnd(gh) { if (!(gh->flags & GCONSOLE_FLG_NOSTORE)) _gwinDrawEnd(gh); } + +void gwinPutChar(GHandle gh, char c) { + #define gcw ((GConsoleObject *)gh) + uint8_t width, fy; + + if (gh->vmt != &consoleVMT || !gh->font) + return; + + fy = gdispGetFontMetric(gh->font, fontHeight); + + #if GWIN_CONSOLE_ESCSEQ + /** + * Handle escape sequences + * 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 + */ + switch (gcw->escstate) { + case 1: + gcw->escstate = 0; + if (ESCtoAttr(c, &gcw->currattr)) { + if (gcw->cx == 0 && gcw->cy == 0) + gcw->startattr = gcw->currattr; + else { + putCharInBuffer(gcw, 27); + putCharInBuffer(gcw, c); + } + } else { + switch(c) { + case 'J': + // Clear the console and reset the cursor + clearBuffer(gcw); + if (DrawStart(gh)) { + gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); + DrawEnd(gh); + } + gcw->cx = 0; + gcw->cy = 0; + gcw->startattr = gcw->currattr; + break; + } + } + return; + } + #endif + + /** + * Special Characters: + * + * Carriage returns and line feeds (\r & \n) are handled in unix terminal cooked mode; that is, + * line feeds perform both actions and carriage-returns are ignored. + * + * if GWIN_CONSOLE_ESCSEQ is turned on then ESC is trapped ready for the escape command. + * + * All other characters are treated as printable. + */ + switch (c) { + case '\n': + // clear to the end of the line + #if GWIN_CONSOLE_USE_CLEAR_LINES + if (gcw->cx == 0 && gcw->cy+fy < gh->height && DrawStart(gh)) { + gdispGFillArea(gh->display, gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor); + DrawEnd(gh); + } + #endif + // update the cursor + gcw->cx = 0; + gcw->cy += fy; + putCharInBuffer(gcw, '\n'); + // We use lazy scrolling here and only scroll when the next char arrives + return; + + case '\r': + // gcw->cx = 0; + return; + + #if GWIN_CONSOLE_ESCSEQ + case 27: // ESC + gcw->escstate = 1; + return; + #endif + } + + // Characters with no width are ignored + if (!(width = gdispGetCharWidth(c, gh->font))) + return; + + // Allow space for (very crude) bold + #if GWIN_CONSOLE_ESCSEQ + if ((gcw->currattr & ESC_BOLD)) + width++; + #endif + + // Do we need to go to the next line to fit this character? + if (gcw->cx + width >= gh->width) { + gcw->cx = 0; + gcw->cy += fy; + putCharInBuffer(gcw, '\n'); + } + + // Do we need to scroll to fit this character? + if (gcw->cy + fy > gh->height) { + #if GWIN_CONSOLE_USE_HISTORY && GWIN_CONSOLE_BUFFER_SCROLLING + if (gcw->buffer) { + // Scroll the buffer and then redraw using the buffer + scrollBuffer(gcw); + if (DrawStart(gh)) { + HistoryRedraw(gh); + DrawEnd(gh); + } + } else + #endif + #if GDISP_NEED_SCROLL + { + // Scroll the console using hardware + scrollBuffer(gcw); + if (DrawStart(gh)) { + gdispGVerticalScroll(gh->display, gh->x, gh->y, gh->width, gh->height, fy, gh->bgcolor); + DrawEnd(gh); + } + + // Set the cursor to the start of the last line + gcw->cx = 0; + gcw->cy = (((coord_t)(gh->height/fy))-1)*fy; + } + #else + { + // Clear the console and reset the cursor + clearBuffer(gcw); + if (DrawStart(gh)) { + gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); + DrawEnd(gh); + } + gcw->cx = 0; + gcw->cy = 0; + #if GWIN_CONSOLE_ESCSEQ + gcw->startattr = gcw->currattr; + #endif + } + #endif + } + + // Save the char + putCharInBuffer(gcw, c); + + // Draw the character + if (DrawStart(gh)) { + + // If we are at the beginning of a new line clear the line + #if GWIN_CONSOLE_USE_CLEAR_LINES + if (gcw->cx == 0) + gdispGFillArea(gh->display, gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor); + #endif + + #if GWIN_CONSOLE_USE_FILLED_CHARS + gdispGFillChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw), gh->bgcolor); + #else + gdispGDrawChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw)); + #endif + + #if GWIN_CONSOLE_ESCSEQ + // Draw the underline + if ((gcw->currattr & ESC_UNDERLINE)) + gdispGDrawLine(gh->display, gh->x + gcw->cx, gh->y + gcw->cy + fy - gdispGetFontMetric(gh->font, fontDescendersHeight), + gh->x + gcw->cx + width + gdispGetFontMetric(gh->font, fontCharPadding), gh->y + gcw->cy + fy - gdispGetFontMetric(gh->font, fontDescendersHeight), + ESCPrintColor(gcw)); + // Bold (very crude) + if ((gcw->currattr & ESC_BOLD)) + gdispGDrawChar(gh->display, gh->x + gcw->cx + 1, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw)); + #endif + + DrawEnd(gh); + } + + // Update the cursor + gcw->cx += width + gdispGetFontMetric(gh->font, fontCharPadding); + + #undef gcw +} + +void gwinPutString(GHandle gh, const char *str) { + while(*str) + gwinPutChar(gh, *str++); +} + +void gwinPutCharArray(GHandle gh, const char *str, size_t n) { + while(n--) + gwinPutChar(gh, *str++); +} + +#include + +#define MAX_FILLER 11 +#define FLOAT_PRECISION 100000 + +static char *ltoa_wd(char *p, long num, unsigned radix, long divisor) { + int i; + char *q; + + if (!divisor) divisor = num; + + q = p + MAX_FILLER; + do { + i = (int)(num % radix); + i += '0'; + if (i > '9') + i += 'A' - '0' - 10; + *--q = i; + num /= radix; + } while ((divisor /= radix) != 0); + + i = (int)(p + MAX_FILLER - q); + do { + *p++ = *q++; + } while (--i); + + return p; +} + +#if GWIN_CONSOLE_USE_FLOAT + static char *ftoa(char *p, double num) { + long l; + unsigned long precision = FLOAT_PRECISION; + + l = num; + p = ltoa_wd(p, l, 10, 0); + *p++ = '.'; + l = (num - l) * precision; + return ltoa_wd(p, l, 10, precision / 10); + } +#endif + +void gwinPrintf(GHandle gh, const char *fmt, ...) { + va_list ap; + char *p, *s, c, filler; + int i, precision, width; + bool_t is_long, left_align; + long l; + #if GWIN_CONSOLE_USE_FLOAT + float f; + char tmpbuf[2*MAX_FILLER + 1]; + #else + char tmpbuf[MAX_FILLER + 1]; + #endif + + if (gh->vmt != &consoleVMT || !gh->font) + return; + + va_start(ap, fmt); + while (TRUE) { + c = *fmt++; + if (c == 0) { + va_end(ap); + return; + } + if (c != '%') { + gwinPutChar(gh, c); + continue; + } + + p = tmpbuf; + s = tmpbuf; + left_align = FALSE; + if (*fmt == '-') { + fmt++; + left_align = TRUE; + } + filler = ' '; + if (*fmt == '.') { + fmt++; + filler = '0'; + } + width = 0; + + while (TRUE) { + c = *fmt++; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c == '*') + c = va_arg(ap, int); + else + break; + width = width * 10 + c; + } + precision = 0; + if (c == '.') { + while (TRUE) { + c = *fmt++; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c == '*') + c = va_arg(ap, int); + else + break; + precision = precision * 10 + c; + } + } + /* Long modifier.*/ + if (c == 'l' || c == 'L') { + is_long = TRUE; + if (*fmt) + c = *fmt++; + } + else + is_long = (c >= 'A') && (c <= 'Z'); + + /* Command decoding.*/ + switch (c) { + case 'c': + filler = ' '; + *p++ = va_arg(ap, int); + break; + case 's': + filler = ' '; + if ((s = va_arg(ap, char *)) == 0) + s = "(null)"; + if (precision == 0) + precision = 32767; + for (p = s; *p && (--precision >= 0); p++); + break; + case 'D': + case 'd': + if (is_long) + l = va_arg(ap, long); + else + l = va_arg(ap, int); + if (l < 0) { + *p++ = '-'; + l = -l; + } + p = ltoa_wd(p, l, 10, 0); + break; + #if GWIN_CONSOLE_USE_FLOAT + case 'f': + f = (float) va_arg(ap, double); + if (f < 0) { + *p++ = '-'; + f = -f; + } + p = ftoa(p, f); + break; + #endif + case 'X': + case 'x': + c = 16; + goto unsigned_common; + case 'U': + case 'u': + c = 10; + goto unsigned_common; + case 'O': + case 'o': + c = 8; + unsigned_common: + if (is_long) + l = va_arg(ap, long); + else + l = va_arg(ap, int); + p = ltoa_wd(p, l, c, 0); + break; + default: + *p++ = c; + break; + } + + i = (int)(p - s); + if ((width -= i) < 0) + width = 0; + if (left_align == FALSE) + width = -width; + if (width < 0) { + if (*s == '-' && filler == '0') { + gwinPutChar(gh, *s++); + i--; + } + do { + gwinPutChar(gh, filler); + } while (++width != 0); + } + while (--i >= 0) + gwinPutChar(gh, *s++); + while (width) { + gwinPutChar(gh, filler); + width--; + } + } +} + +#endif /* GFX_USE_GWIN && GWIN_NEED_CONSOLE */ + + diff --git a/src/gwin/gwin_console.h b/src/gwin/gwin_console.h new file mode 100644 index 00000000..77e623eb --- /dev/null +++ b/src/gwin/gwin_console.h @@ -0,0 +1,178 @@ +/* + * 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_console.h + * @brief GWIN Graphic window subsystem header file. + * + * @defgroup Console Console + * @ingroup Windows + * + * @details GWIN allows it to create a console/terminal like window. + * You can simply use chprintf() to print to the terminal. + * + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_CONSOLE must be set to TRUE in your gfxconf.h + * + * @{ + */ + +#ifndef _GWIN_CONSOLE_H +#define _GWIN_CONSOLE_H + +/* This file is included within "src/gwin/sys_defs.h" */ + +// A console window. Supports wrapped text writing and a cursor. +typedef struct GConsoleObject { + GWindowObject g; + coord_t cx, cy; // Cursor position + + #if GWIN_CONSOLE_ESCSEQ + uint8_t startattr; // ANSI-like escape sequences + uint8_t currattr; + uint16_t escstate; + #endif + + #if GWIN_CONSOLE_USE_HISTORY + char * buffer; // buffer to store console content + size_t bufsize; // size of buffer + size_t bufpos; // the position of the next char + #endif + + #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM + struct GConsoleWindowStream_t { + const struct GConsoleWindowVMT_t *vmt; + _base_asynchronous_channel_data + } stream; + #endif + +} GConsoleObject; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a console window. + * @details A console window allows text to be written. + * @note Text in a console window supports newlines and will wrap text as required. + * @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] gc The GConsoleObject structure to initialise. If this is NULL the structure is dynamically allocated. + * @param[in] pInit The initialization parameters to use + * + * @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 On creation even if the window is visible it is not automatically cleared. + * You may do that by calling @p gwinClear() (possibly after changing your background color) + * @note A console does not save the drawing state. It is not automatically redrawn if the window is moved or + * its visibility state is changed. + * + * @api + */ +GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *pInit); +#define gwinConsoleCreate(gc, pInit) gwinGConsoleCreate(GDISP, gc, pInit) + +#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM + /** + * @brief Get a stream from a console window suitable for use with chprintf(). + * @return The stream handle or NULL if this is not a console window. + * + * @param[in] gh The window handle (must be a console window) + * + * @note Only useful in ChibiOS + * + * @api + */ + BaseSequentialStream *gwinConsoleGetStream(GHandle gh); +#endif + +#if GWIN_CONSOLE_USE_HISTORY + /** + * @brief Assign a buffer to keep track of the content while the widget is invisible. + * @pre GWIN_CONSOLE_USE_HISTORY must be set to TRUE in your gfxconf.h + * + * @param[in] gh The window handle (must be a console window) + * @param[in] onoff If TRUE a buffer is allocated to maintain console text + * when the console is obscured or invisible. If FALSE, then + * any existing buffer is deallocated. + * @note When the history buffer is turned on, scrolling is implemented using the + * history buffer. + * + * @return TRUE if the history buffer is now turned on. + */ + bool_t gwinConsoleSetBuffer(GHandle gh, bool_t onoff); +#endif + +/** + * @brief Put a character at the cursor position in the window. + * @note Uses the current foreground color to draw the character and fills the background using the background drawing color + * + * @param[in] gh The window handle (must be a console window) + * @param[in] c The character to draw + * + * @api + */ +void gwinPutChar(GHandle gh, char c); + +/** + * @brief Put a string at the cursor position in the window. It will wrap lines as required. + * @note Uses the current foreground color to draw the string and fills the background using the background drawing color + * + * @param[in] gh The window handle (must be a console window) + * @param[in] str The string to draw + * + * @api + */ +void gwinPutString(GHandle gh, const char *str); + +/** + * @brief Put the character array at the cursor position in the window. It will wrap lines as required. + * @note Uses the current foreground color to draw the string and fills the background using the background drawing color + * + * @param[in] gh The window handle (must be a console window) + * @param[in] str The string to draw + * @param[in] n The number of characters to draw + * + * @api + */ +void gwinPutCharArray(GHandle gh, const char *str, size_t n); + +/** + * @brief Print a formatted string at the cursor position in the window. It will wrap lines as required. + * @details This function implements a minimal printf() like functionality + * The general parameters format is: %[-][width|*][.precision|*][l|L]p. + * The following parameter types (p) are supported: + * - x hexadecimal integer. + * - X hexadecimal long. + * - o octal integer. + * - O octal long. + * - d decimal signed integer. + * - D decimal signed long. + * - u decimal unsigned integer. + * - U decimal unsigned long. + * - c character. + * - s string. + * @note Uses the current foreground color to draw the string and fills the background using the background drawing color + * + * @param[in] gh The window handle (must be a console window) + * @param[in] fmt The format string (as per printf) + * @param[in] ... The format string arguments. + * + * @api + */ +void gwinPrintf(GHandle gh, const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _GWIN_CONSOLE_H */ +/** @} */ diff --git a/src/gwin/gwin_container.c b/src/gwin/gwin_container.c new file mode 100644 index 00000000..3145ae56 --- /dev/null +++ b/src/gwin/gwin_container.c @@ -0,0 +1,201 @@ +/* + * 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_container.c + * @brief GWIN sub-system container code + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_CONTAINERS + +#include "gwin_class.h" + +void _gcontainerInit(void) +{ +} + +void _gcontainerDeinit(void) +{ +} + +GHandle _gcontainerCreate(GDisplay *g, GContainerObject *pgc, const GWidgetInit *pInit, const gcontainerVMT *vmt) { + if (!(pgc = (GContainerObject *)_gwidgetCreate(g, (GWidgetObject *)pgc, pInit, &vmt->gw))) + return 0; + + pgc->g.flags |= GWIN_FLG_CONTAINER; + + return &pgc->g; +} + +void _gcontainerDestroy(GHandle gh) { + GHandle child; + + while((child = gwinGetFirstChild(gh))) + gwinDestroy(child); + _gwidgetDestroy(gh); +} + +GHandle gwinGetFirstChild(GHandle gh) { + GHandle child; + + for(child = gwinGetNextWindow(0); child; child = gwinGetNextWindow(child)) + if (child->parent == gh) + return child; + return 0; +} + +GHandle gwinGetSibling(GHandle gh) { + GHandle child; + + for(child = gwinGetNextWindow(gh), gh = gh->parent; child; child = gwinGetNextWindow(child)) + if (child->parent == gh) + return child; + return 0; +} + +coord_t gwinGetInnerWidth(GHandle gh) { + if (!(gh->flags & GWIN_FLG_CONTAINER)) + return 0; + return gh->width - ((const gcontainerVMT *)gh->vmt)->LeftBorder(gh) - ((const gcontainerVMT *)gh->vmt)->RightBorder(gh); +} + +coord_t gwinGetInnerHeight(GHandle gh) { + if (!(gh->flags & GWIN_FLG_CONTAINER)) + return 0; + return gh->height - ((const gcontainerVMT *)gh->vmt)->TopBorder(gh) - ((const gcontainerVMT *)gh->vmt)->BottomBorder(gh); +} + +#endif /* GFX_USE_GWIN && GWIN_NEED_CONTAINERS */ +/** @} */ + +/*----------------------------------------------- + * The simplest container type - a container + *----------------------------------------------- + * + * @defgroup Containers Containers + * @ingroup GWIN + * + * @{ + */ + +#if GFX_USE_GWIN && GWIN_NEED_CONTAINER + +#if GWIN_CONTAINER_BORDER != GWIN_FIRST_CONTROL_FLAG + #error "GWIN Container: - Flag definitions don't match" +#endif + +#define BORDER_WIDTH 2 + +static coord_t BorderSize(GHandle gh) { return (gh->flags & GWIN_CONTAINER_BORDER) ? BORDER_WIDTH : 0; } + +// The container VMT table +static const gcontainerVMT containerVMT = { + { + { + "Container", // The classname + sizeof(GContainerObject), // The object size + _gcontainerDestroy, // The destroy routine + _gcontainerRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinContainerDraw_Std, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + 0, 0, 0, // No mouse + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 0, 0, 0, 0, 0, // No toggles + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, 0, 0, 0, // No dials + }, + #endif + }, + BorderSize, // The size of the left border (mandatory) + BorderSize, // The size of the top border (mandatory) + BorderSize, // The size of the right border (mandatory) + BorderSize, // The size of the bottom border (mandatory) + 0, // A child has been added (optional) + 0, // A child has been deleted (optional) +}; + +GHandle gwinGContainerCreate(GDisplay *g, GContainerObject *gc, const GWidgetInit *pInit, uint32_t flags) { + if (!(gc = (GContainerObject *)_gcontainerCreate(g, gc, pInit, &containerVMT))) + return 0; + + gc->g.flags |= (flags & GWIN_CONTAINER_BORDER); + + gwinSetVisible((GHandle)gc, pInit->g.show); + return (GHandle)gc; +} + +void gwinContainerDraw_Transparent(GWidgetObject *gw, void *param) { + (void)param; + + if (gw->g.vmt != (gwinVMT *)&containerVMT) + return; + + if ((gw->g.flags & GWIN_CONTAINER_BORDER)) + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); + + // Don't touch the client area +} + +void gwinContainerDraw_Std(GWidgetObject *gw, void *param) { + (void)param; + + if (gw->g.vmt != (gwinVMT *)&containerVMT) + return; + + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); + gwinContainerDraw_Transparent(gw, param); +} + +#if GDISP_NEED_IMAGE + void gwinContainerDraw_Image(GWidgetObject *gw, void *param) { + #define gi ((gdispImage *)param) + coord_t x, y, iw, ih, mx, my; + + if (gw->g.vmt != (gwinVMT *)&containerVMT) + return; + + // Draw the frame + gwinContainerDraw_Transparent(gw, param); + + // Draw the client area by tiling the image + mx = gw->g.x+gw->g.width; + my = gw->g.y+gw->g.height; + y = gw->g.y; + if ((gw->g.flags & GWIN_CONTAINER_BORDER)) { + mx--; + my--; + y++; + } + for(ih=gi->height; y < my; y += ih) { + if (ih > my - y) + ih = my - y; + x = gw->g.x; + if ((gw->g.flags & GWIN_CONTAINER_BORDER)) + x++; + for(iw=gi->width; x < mx; x += iw) { + if (iw > mx - x) + iw = mx - x; + gdispGImageDraw(gw->g.display, gi, x, y, ih, iw, 0, 0); + } + } + + #undef gi + } +#endif + +#endif diff --git a/src/gwin/gwin_container.h b/src/gwin/gwin_container.h new file mode 100644 index 00000000..ff1c1ce9 --- /dev/null +++ b/src/gwin/gwin_container.h @@ -0,0 +1,161 @@ +/* + * 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_container.h + * + * @defgroup Container Container + * @ingroup Containers + * + * @details A Container is a GWindow that supports child windows. It is also + * a widget in its own right and therefore can accept user input directly. + * + * @pre GFX_USE_GWIN and GWIN_NEED_CONTAINERS must be set to TRUE in your gfxconf.h + * @{ + */ + +#ifndef _GCONTAINER_H +#define _GCONTAINER_H + +/* This file is included within "src/gwin/sys_defs.h" */ + +// Forward definition +struct GContainerObject; + +/** + * @brief The GWIN Container structure + * @note A container is a GWIN widget that can have children. + * @note Do not access the members directly. Treat it as a black-box and use the method functions. + * + * @{ + */ +typedef GWidgetObject GContainerObject; +/** @} */ + +/** + * A comment/rant on the above structure: + * We would really like the GWidgetObject member to be anonymous. While this is + * allowed under the C11, C99, GNU and various other standards which have been + * around forever - compiler support often requires special flags e.g + * gcc requires the -fms-extensions flag (no wonder the language and compilers have + * not really progressed in 30 years). As portability is a key requirement + * we unfortunately won't use this useful feature in case we get a compiler that + * won't support it even with special flags. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Get the first child window + * + * @return The first child or NULL if are no children windows + * + * @param[in] gh The parent container or NULL to get the first top level window + * + * @api + */ + GHandle gwinGetFirstChild(GHandle gh); + + /** + * @brief Get the next child window in the z-order + * + * @return The next window or NULL if no more children + * + * @param[in] gh The window to obtain the next sibling of. + * + * @note This returns the next window under the current parent window. + * Unlike @p gwinGetNextWindow() it will only return windows that + * have the same parent as the supplied window. + * + * @api + */ + GHandle gwinGetSibling(GHandle gh); + + /** + * @brief Get the inner width of a container window + * + * @return The inner width of a container window or zero if this is not a container + * + * @param[in] gh The window + * + * @api + */ + coord_t gwinGetInnerWidth(GHandle gh); + + /** + * @brief Get the inner height of a container window + * + * @return The inner height of a container window or zero if this is not a container + * + * @param[in] gh The window + * + * @api + */ + coord_t gwinGetInnerHeight(GHandle gh); + + + /** + * @brief Flags for gwinContainerCreate() + * @{ + */ + #define GWIN_CONTAINER_BORDER 0x00000001 + /** @} */ + + /** + * @brief Create a simple container. + * @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] gw The GContainerObject structure to initialise. If this is NULL the structure is dynamically allocated. + * @param[in] pInit The initialisation parameters + * @param[in] flags Some flags, see notes + * + * @api + */ + GHandle gwinGContainerCreate(GDisplay *g, GContainerObject *gw, const GWidgetInit *pInit, uint32_t flags); + #define gwinContainerCreate(gc, pInit, flags) gwinGContainerCreate(GDISP, gc, pInit, flags) + + /** + * @brief The custom draw routines for a simple container + * @details These function may be passed to @p gwinSetCustomDraw() to get different frame drawing styles + * + * @param[in] gw The widget object (in this case a frame) + * @param[in] param A parameter passed in from the user + * + * @note In your own custom drawing function you may optionally call these + * standard functions and then draw your extra details on top. + * + * @note gwinContainerDraw_Std() will fill the client area with the background color.
+ * gwinContainerDraw_Transparent() will not fill the client area at all.
+ * gwinContainerDraw_Image() will tile the image throughout the client area.
+ * All these drawing functions draw the frame itself the same way. + * + * @note The standard functions below ignore the param parameter except for @p gwinContainerDraw_Image(). + * @note The image custom draw function @p gwinContainerDraw_Image() uses param to pass in the gdispImage pointer. + * The image must be already opened before calling @p gwinSetCustomDraw(). + * + * @api + * @{ + */ + void gwinContainerDraw_Std(GWidgetObject *gw, void *param); + void gwinContainerDraw_Transparent(GWidgetObject *gw, void *param); + void gwinContainerDraw_Image(GWidgetObject *gw, void *param); + /** @} */ + +#ifdef __cplusplus +} +#endif + +/* Include extra container types */ +#if GWIN_NEED_FRAME || defined(__DOXYGEN__) + #include "gwin_frame.h" +#endif + +#endif /* _GCONTAINER_H */ +/** @} */ diff --git a/src/gwin/gwin_frame.c b/src/gwin/gwin_frame.c new file mode 100644 index 00000000..63df3be2 --- /dev/null +++ b/src/gwin/gwin_frame.c @@ -0,0 +1,322 @@ +/* + * 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_frame.c + * @brief GWIN sub-system frame code. + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_FRAME + +#include "gwin_class.h" + +/* Some position values */ +#define BUTTON_X 18 // Button Width +#define BUTTON_Y 18 // Button Height +#define BUTTON_I 3 // Button inner margin +#define BUTTON_T 2 // Gap from top of window to button +#define BUTTON_B 2 // Gap from button to the bottom of the frame title area +#define BORDER_L 2 // Left Border +#define BORDER_R 2 // Right Border +#define BORDER_T (BUTTON_Y+BUTTON_T+BUTTON_B) // Top Border (Title area) +#define BORDER_B 2 // Bottom Border + +/* Internal state flags */ +#define GWIN_FRAME_USER_FLAGS (GWIN_FRAME_CLOSE_BTN|GWIN_FRAME_MINMAX_BTN) +#define GWIN_FRAME_CLOSE_PRESSED (GWIN_FRAME_MINMAX_BTN << 1) +#define GWIN_FRAME_MIN_PRESSED (GWIN_FRAME_MINMAX_BTN << 2) +#define GWIN_FRAME_MAX_PRESSED (GWIN_FRAME_MINMAX_BTN << 3) +#define GWIN_FRAME_REDRAW_FRAME (GWIN_FRAME_MINMAX_BTN << 4) // Only redraw the frame +#if GWIN_FRAME_CLOSE_BTN < GWIN_FIRST_CONTROL_FLAG + #error "GWIN Frame: - Flag definitions don't match" +#endif +#if GWIN_FRAME_REDRAW_FRAME > GWIN_LAST_CONTROL_FLAG + #error "GWIN Frame: - Flag definitions don't match" +#endif + +static coord_t BorderSizeL(GHandle gh) { (void)gh; return BORDER_L; } +static coord_t BorderSizeR(GHandle gh) { (void)gh; return BORDER_R; } +static coord_t BorderSizeT(GHandle gh) { (void)gh; return BORDER_T; } +static coord_t BorderSizeB(GHandle gh) { (void)gh; return BORDER_B; } + +static void forceFrameRedraw(GWidgetObject *gw) { + // Force a redraw of just the frame. + // This is a big naughty but who really cares. + gw->g.flags |= GWIN_FRAME_REDRAW_FRAME; + gw->fnDraw(gw, gw->fnParam); + gw->g.flags &= ~GWIN_FRAME_REDRAW_FRAME; +} + +#if GINPUT_NEED_MOUSE + static void mouseDown(GWidgetObject *gw, coord_t x, coord_t y) { + coord_t pos; + + // We must be clicking on the frame button area to be of interest + if (y < BUTTON_T && y >= BUTTON_T+BUTTON_Y) + return; + + pos = gw->g.width - (BORDER_R+BUTTON_X); + if ((gw->g.flags & GWIN_FRAME_CLOSE_BTN)) { + if (x >= pos && x < pos+BUTTON_X) { + // Close is pressed - force redraw the frame only + gw->g.flags |= GWIN_FRAME_CLOSE_PRESSED; + forceFrameRedraw(gw); + return; + } + pos -= BUTTON_X; + } + if ((gw->g.flags & GWIN_FRAME_MINMAX_BTN)) { + if (x >= pos && x < pos+BUTTON_X) { + // Close is pressed - force redraw the frame only + gw->g.flags |= GWIN_FRAME_MAX_PRESSED; + forceFrameRedraw(gw); + return; + } + pos -= BUTTON_X; + if (x >= pos && x < pos+BUTTON_X) { + // Close is pressed - force redraw the frame only + gw->g.flags |= GWIN_FRAME_MIN_PRESSED; + forceFrameRedraw(gw); + return; + } + pos -= BUTTON_X; + } + } + + static void mouseUp(GWidgetObject *gw, coord_t x, coord_t y) { + #if GWIN_BUTTON_LAZY_RELEASE + if ((gw->g.flags & GWIN_FRAME_CLOSE_PRESSED)) { + // Close is released - destroy the window + gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); + forceFrameRedraw(gw); + _gwinSendEvent(&gw->g, GEVENT_GWIN_CLOSE); + gwinDestroy(&gw->g); + return; + } + if ((gw->g.flags & GWIN_FRAME_MAX_PRESSED)) { + // Max is released - maximize the window + gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); + forceFrameRedraw(gw); + gwinSetMinMax(&gw->g, GWIN_MAXIMIZE); + return; + } + if ((gw->g.flags & GWIN_FRAME_MIN_PRESSED)) { + // Min is released - minimize the window + gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); + forceFrameRedraw(gw); + gwinSetMinMax(&gw->g, GWIN_MINIMIZE); + return; + } + #else + // If nothing is pressed we have nothing to do. + if (!(gw->g.flags & (GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED))) + return; + + // We must be releasing over the button + if (y >= BUTTON_T && y < BUTTON_T+BUTTON_Y) { + coord_t pos; + + pos = gw->g.width - (BORDER_R+BUTTON_X); + if ((gw->g.flags & GWIN_FRAME_CLOSE_BTN)) { + if ((gw->g.flags & GWIN_FRAME_CLOSE_PRESSED) && x >= pos && x <= pos+BUTTON_X) { + // Close is released - destroy the window. This is tricky as we already have the drawing lock. + gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); + forceFrameRedraw(gw); + _gwinDestroy(&gw->g, REDRAW_INSESSION); + return; + } + pos -= BUTTON_X; + } + if ((gw->g.flags & GWIN_FRAME_MINMAX_BTN)) { + if ((gw->g.flags & GWIN_FRAME_MAX_PRESSED) && x >= pos && x <= pos+BUTTON_X) { + // Max is released - maximize the window + gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); + forceFrameRedraw(gw); + gwinSetMinMax(&gw->g, GWIN_MAXIMIZE); + return; + } + pos -= BUTTON_X; + if ((gw->g.flags & GWIN_FRAME_MIN_PRESSED) && x >= pos && x <= pos+BUTTON_X) { + // Min is released - minimize the window + gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); + forceFrameRedraw(gw); + gwinSetMinMax(&gw->g, GWIN_MINIMIZE); + return; + } + pos -= BUTTON_X; + } + } + + // Clear any flags and redraw the frame + gw->g.flags &= ~(GWIN_FRAME_CLOSE_PRESSED|GWIN_FRAME_MAX_PRESSED|GWIN_FRAME_MIN_PRESSED); + forceFrameRedraw(gw); + #endif + } +#endif + +static const gcontainerVMT frameVMT = { + { + { + "Frame", // The classname + sizeof(GFrameObject), // The object size + _gcontainerDestroy, // The destroy routine + _gcontainerRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinFrameDraw_Std, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + mouseDown, // Process mouse down event + mouseUp, // Process mouse up events + 0, // Process mouse move events + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 0, // 1 toggle role + 0, // Assign Toggles + 0, // Get Toggles + 0, // Process toggle off events + 0, // Process toggle on events + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, // 1 dial roles + 0, // Assign Dials + 0, // Get Dials + 0, // Process dial move events + }, + #endif + }, + BorderSizeL, // The size of the left border (mandatory) + BorderSizeT, // The size of the top border (mandatory) + BorderSizeR, // The size of the right border (mandatory) + BorderSizeB, // The size of the bottom border (mandatory) + 0, // A child has been added (optional) + 0, // A child has been deleted (optional) +}; + +GHandle gwinGFrameCreate(GDisplay *g, GFrameObject *fo, GWidgetInit *pInit, uint32_t flags) { + if (!(fo = (GFrameObject *)_gcontainerCreate(g, (GContainerObject *)fo, pInit, &frameVMT))) + return 0; + + // Make sure we only have "safe" flags. + flags &= GWIN_FRAME_USER_FLAGS; + + /* Apply flags. We apply these here so the controls above are outside the child area */ + fo->g.flags |= flags; + + gwinSetVisible(&fo->g, pInit->g.show); + + return &fo->g; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Default render routines // +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void gwinFrameDraw_Transparent(GWidgetObject *gw, void *param) { + const GColorSet *pcol; + coord_t pos; + color_t contrast; + color_t btn; + (void)param; + + if (gw->g.vmt != (gwinVMT *)&frameVMT) + return; + + pcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled; + contrast = gdispContrastColor(pcol->edge); + btn = gdispBlendColor(pcol->edge, contrast, 128); + + // Render the frame + gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, BORDER_T, gw->text, gw->g.font, contrast, pcol->edge, justifyCenter); + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+BORDER_T, BORDER_L, gw->g.height-(BORDER_T+BORDER_B), pcol->edge); + gdispGFillArea(gw->g.display, gw->g.x+gw->g.width-BORDER_R, gw->g.y+BORDER_T, BORDER_R, gw->g.height-(BORDER_T+BORDER_B), pcol->edge); + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+gw->g.height-BORDER_B, gw->g.width, BORDER_B, pcol->edge); + + // Add the buttons + pos = gw->g.x+gw->g.width - (BORDER_R+BUTTON_X); + + if ((gw->g.flags & GWIN_FRAME_CLOSE_BTN)) { + if ((gw->g.flags & GWIN_FRAME_CLOSE_PRESSED)) + gdispFillArea(pos, gw->g.y+BUTTON_T, BUTTON_X, BUTTON_Y, btn); + gdispDrawLine(pos+BUTTON_I, gw->g.y+(BUTTON_T+BUTTON_I), pos+(BUTTON_X-BUTTON_I-1), gw->g.y+(BUTTON_T+BUTTON_Y-BUTTON_I-1), contrast); + gdispDrawLine(pos+(BUTTON_X-BUTTON_I-1), gw->g.y+(BUTTON_T+BUTTON_I), pos+BUTTON_I, gw->g.y+(BUTTON_T+BUTTON_Y-BUTTON_I-1), contrast); + pos -= BUTTON_X; + } + + if ((gw->g.flags & GWIN_FRAME_MINMAX_BTN)) { + if ((gw->g.flags & GWIN_FRAME_MAX_PRESSED)) + gdispFillArea(pos, gw->g.y+BUTTON_T, BUTTON_X, BUTTON_Y, btn); + // the symbol + gdispDrawBox(pos+BUTTON_I, gw->g.y+(BUTTON_T+BUTTON_I), BUTTON_X-2*BUTTON_I, BUTTON_Y-2*BUTTON_I, contrast); + gdispDrawLine(pos+(BUTTON_I+1), gw->g.y+(BUTTON_T+BUTTON_I+1), pos+(BUTTON_X-BUTTON_I-2), gw->g.y+(BUTTON_T+BUTTON_I+1), contrast); + gdispDrawLine(pos+(BUTTON_I+1), gw->g.y+(BUTTON_T+BUTTON_I+2), pos+(BUTTON_X-BUTTON_I-2), gw->g.y+(BUTTON_T+BUTTON_I+2), contrast); + pos -= BUTTON_X; + if ((gw->g.flags & GWIN_FRAME_MIN_PRESSED)) + gdispFillArea(pos, gw->g.y+BUTTON_T, BUTTON_X, BUTTON_Y, btn); + gdispDrawLine(pos+BUTTON_I, gw->g.y+(BUTTON_T+BUTTON_Y-BUTTON_I-1), pos+(BUTTON_X-BUTTON_I-1), gw->g.y+(BUTTON_T+BUTTON_Y-BUTTON_I-1), contrast); + pos -= BUTTON_X; + } + + // Don't touch the client area +} + +void gwinFrameDraw_Std(GWidgetObject *gw, void *param) { + (void)param; + + if (gw->g.vmt != (gwinVMT *)&frameVMT) + return; + + // Draw the frame + gwinFrameDraw_Transparent(gw, param); + + // Drop out if that is all we want to draw + if ((gw->g.flags & GWIN_FRAME_REDRAW_FRAME)) + return; + + // Draw the client area + gdispGFillArea(gw->g.display, gw->g.x + BORDER_L, gw->g.y + BORDER_T, gw->g.width - (BORDER_L+BORDER_R), gw->g.height - (BORDER_T+BORDER_B), gw->pstyle->background); +} + +#if GDISP_NEED_IMAGE + void gwinFrameDraw_Image(GWidgetObject *gw, void *param) { + #define gi ((gdispImage *)param) + coord_t x, y, iw, ih, mx, my; + + if (gw->g.vmt != (gwinVMT *)&frameVMT) + return; + + // Draw the frame + gwinFrameDraw_Transparent(gw, param); + + // Drop out if that is all we want to draw + if ((gw->g.flags & GWIN_FRAME_REDRAW_FRAME)) + return; + + // Draw the client area by tiling the image + mx = gw->g.x+gw->g.width - BORDER_R; + my = gw->g.y+gw->g.height - BORDER_B; + for(y = gw->g.y+BORDER_T, ih = gi->height; y < my; y += ih) { + if (ih > my - y) + ih = my - y; + for(x = gw->g.x+BORDER_L; iw = gi->width; x < mx; x += iw) { + if (iw > mx - x) + iw = mx - x; + gdispGImageDraw(gw->g.display, gi, x, y, ih, iw, 0, 0); + } + } + + #undef gi + } +#endif + +#endif /* (GFX_USE_GWIN && GWIN_NEED_FRAME) || defined(__DOXYGEN__) */ diff --git a/src/gwin/gwin_frame.h b/src/gwin/gwin_frame.h new file mode 100644 index 00000000..f71a88a5 --- /dev/null +++ b/src/gwin/gwin_frame.h @@ -0,0 +1,97 @@ +/* + * 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_frame.h + * @brief GWIN Graphic window subsystem header file. + * + * @defgroup Frame Frame + * @ingroup Containers + * + * @details A frame is a rectangular window that can have optional border as well as buttons to + * close, maximize and minimize it. The main purpose of this widget is to contain children. + * + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_FRAME must be set to TRUE in your gfxconf.h + * @{ + */ + +#ifndef _GWIN_FRAME_H +#define _GWIN_FRAME_H + +/* This file is included from src/gwin/gwin_container.h */ + +/** + * @brief Flags for gwinFrameCreate() + * @{ + */ +#define GWIN_FRAME_BORDER 0x00000000 // Deprecated. A border is always shown with a frame window now. +#define GWIN_FRAME_CLOSE_BTN 0x00000001 +#define GWIN_FRAME_MINMAX_BTN 0x00000002 +/** @} */ + +typedef GContainerObject GFrameObject; + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Create a frame widget + * + * @details This widget provides a window like we know it from desktop systems. + * + * @param[in] g The GDisplay to display this window on + * @param[in] fo The GFrameObject structure to initialize. If this is NULL the structure is dynamically allocated. + * @param[in] pInit The initialization parameters + * @param[in] flags Some flags, see notes. + * + * @note Possible flags are: GWIN_FRAME_CLOSE_BTN, GWIN_FRAME_MINMAX_BTN. + * @note These frame buttons are processed internally. The close button will invoke a gwinDestroy() which will + * destroy the window itself and EVERY child it contains (also children of children). + * + * @return NULL if there is no resulting widget. A valid GHandle otherwise. + * + * @api + */ + GHandle gwinGFrameCreate(GDisplay *g, GFrameObject *fo, GWidgetInit *pInit, uint32_t flags); + #define gwinFrameCreate(fo, pInit, flags) gwinGFrameCreate(GDISP, fo, pInit, flags); + + /** + * @brief The custom draw routines for a frame window + * @details These function may be passed to @p gwinSetCustomDraw() to get different frame drawing styles + * + * @param[in] gw The widget object (in this case a frame) + * @param[in] param A parameter passed in from the user + * + * @note In your own custom drawing function you may optionally call these + * standard functions and then draw your extra details on top. + * + * @note gwinFrameDraw_Std() will fill the client area with the background color.
+ * gwinFrameDraw_Transparent() will not fill the client area at all.
+ * gwinFrameDraw_Image() will tile the image throughout the client area.
+ * All these drawing functions draw the frame itself the same way. + * + * @note The standard functions below ignore the param parameter except for @p gwinFrameDraw_Image(). + * @note The image custom draw function @p gwinFrameDraw_Image() uses param to pass in the gdispImage pointer. + * The image must be already opened before calling @p gwinSetCustomDraw(). + * + * @api + * @{ + */ + void gwinFrameDraw_Std(GWidgetObject *gw, void *param); + void gwinFrameDraw_Transparent(GWidgetObject *gw, void *param); + void gwinFrameDraw_Image(GWidgetObject *gw, void *param); + /** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GWIN_FRAME_H */ +/** @} */ + diff --git a/src/gwin/gwin_gl3d.c b/src/gwin/gwin_gl3d.c index e04875c0..6e16479e 100644 --- a/src/gwin/gwin_gl3d.c +++ b/src/gwin/gwin_gl3d.c @@ -18,7 +18,7 @@ #error "GWIN: GL3D only support GDISP_PIXELFORMAT_RGB565 color format (TinyGL limitation)" #endif -#include "src/gwin/class_gwin.h" +#include "gwin_class.h" #include "3rdparty/tinygl-0.4-ugfx/src/zgl.h" diff --git a/src/gwin/gwin_gl3d.h b/src/gwin/gwin_gl3d.h index ca3edf6f..644f45bf 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 "gwin/gwin.h" */ +/* This file is included within "src/gwin/sys_defs.h" */ // A gl3d window diff --git a/src/gwin/gwin_graph.c b/src/gwin/gwin_graph.c new file mode 100644 index 00000000..8a450a9f --- /dev/null +++ b/src/gwin/gwin_graph.c @@ -0,0 +1,338 @@ +/* + * 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_graph.c + * @brief GWIN sub-system button code + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_GRAPH + +#include "gwin_class.h" + +#define GGRAPH_FLG_CONNECTPOINTS (GWIN_FIRST_CONTROL_FLAG<<0) +#define GGRAPH_ARROW_SIZE 5 + +static const GGraphStyle GGraphDefaultStyle = { + { GGRAPH_POINT_DOT, 0, White }, // point + { GGRAPH_LINE_DOT, 2, Gray }, // line + { GGRAPH_LINE_SOLID, 0, White }, // x axis + { GGRAPH_LINE_SOLID, 0, White }, // y axis + { GGRAPH_LINE_NONE, 0, White, 0 }, // x grid + { GGRAPH_LINE_NONE, 0, White, 0 }, // y grid + GWIN_GRAPH_STYLE_XAXIS_ARROWS|GWIN_GRAPH_STYLE_YAXIS_ARROWS // flags +}; + +static const gwinVMT graphVMT = { + "Graph", // The classname + sizeof(GGraphObject), // The object size + 0, // The destroy routine + 0, // The redraw routine + 0, // The after-clear routine +}; + +static void pointto(GGraphObject *gg, coord_t x, coord_t y, const GGraphPointStyle *style) { + if (style->type == GGRAPH_POINT_NONE) + return; + + // Convert to device space. Note the y-axis is inverted. + x += gg->g.x + gg->xorigin; + y = gg->g.y + gg->g.height - 1 - gg->yorigin - y; + + if (style->size <= 1) { + gdispGDrawPixel(gg->g.display, x, y, style->color); + return; + } + + switch(style->type) { + case GGRAPH_POINT_SQUARE: + gdispGDrawBox(gg->g.display, x-style->size, y-style->size, 2*style->size, 2*style->size, style->color); + break; +#if GDISP_NEED_CIRCLE + case GGRAPH_POINT_CIRCLE: + gdispGDrawCircle(gg->g.display, x, y, style->size, style->color); + break; +#endif + case GGRAPH_POINT_DOT: + default: + gdispGDrawPixel(gg->g.display, x, y, style->color); + break; + } +} + +static void lineto(GGraphObject *gg, coord_t x0, coord_t y0, coord_t x1, coord_t y1, const GGraphLineStyle *style) { + coord_t dy, dx; + coord_t addx, addy; + coord_t P, diff, i; + coord_t run_on, run_off, run; + + if (style->type == GGRAPH_LINE_NONE) + return; + + // Convert to device space. Note the y-axis is inverted. + x0 += gg->g.x + gg->xorigin; + y0 = gg->g.y + gg->g.height - 1 - gg->yorigin - y0; + x1 += gg->g.x + gg->xorigin; + y1 = gg->g.y + gg->g.height - 1 - gg->yorigin - y1; + + if (style->size <= 0) { + // Use the driver to draw a solid line + gdispGDrawLine(gg->g.display, x0, y0, x1, y1, style->color); + return; + } + + switch (style->type) { + case GGRAPH_LINE_DOT: + run_on = 1; + run_off = -style->size; + break; + + case GGRAPH_LINE_DASH: + run_on = style->size; + run_off = -style->size; + break; + + case GGRAPH_LINE_SOLID: + default: + // Use the driver to draw a solid line + gdispGDrawLine(gg->g.display, x0, y0, x1, y1, style->color); + return; + } + + // Use Bresenham's algorithm modified to draw a stylized line + run = 0; + if (x1 >= x0) { + dx = x1 - x0; + addx = 1; + } else { + dx = x0 - x1; + addx = -1; + } + if (y1 >= y0) { + dy = y1 - y0; + addy = 1; + } else { + dy = y0 - y1; + addy = -1; + } + + if (dx >= dy) { + dy *= 2; + P = dy - dx; + diff = P - dx; + + for(i=0; i<=dx; ++i) { + if (run++ >= 0) { + if (run >= run_on) + run = run_off; + gdispGDrawPixel(gg->g.display, x0, y0, style->color); + } + if (P < 0) { + P += dy; + x0 += addx; + } else { + P += diff; + x0 += addx; + y0 += addy; + } + } + } else { + dx *= 2; + P = dx - dy; + diff = P - dy; + + for(i=0; i<=dy; ++i) { + if (run++ >= 0) { + if (run >= run_on) + run = run_off; + gdispGDrawPixel(gg->g.display, x0, y0, style->color); + } + if (P < 0) { + P += dx; + y0 += addy; + } else { + P += diff; + x0 += addx; + y0 += addy; + } + } + } +} + +GHandle gwinGGraphCreate(GDisplay *g, GGraphObject *gg, const GWindowInit *pInit) { + if (!(gg = (GGraphObject *)_gwindowCreate(g, &gg->g, pInit, &graphVMT, 0))) + return 0; + gg->xorigin = gg->yorigin = 0; + gg->lastx = gg->lasty = 0; + gwinGraphSetStyle((GHandle)gg, &GGraphDefaultStyle); + gwinSetVisible((GHandle)gg, pInit->show); + return (GHandle)gg; +} + +void gwinGraphSetStyle(GHandle gh, const GGraphStyle *pstyle) { + #define gg ((GGraphObject *)gh) + + if (gh->vmt != &graphVMT) + return; + + gg->style.point = pstyle->point; + gg->style.line = pstyle->line; + gg->style.xaxis = pstyle->xaxis; + gg->style.yaxis = pstyle->yaxis; + gg->style.xgrid = pstyle->xgrid; + gg->style.ygrid = pstyle->ygrid; + gg->style.flags = pstyle->flags; + + #undef gg +} + +void gwinGraphSetOrigin(GHandle gh, coord_t x, coord_t y) { + #define gg ((GGraphObject *)gh) + + if (gh->vmt != &graphVMT) + return; + + gg->xorigin = x; + gg->yorigin = y; + + #undef gg +} + +void gwinGraphDrawAxis(GHandle gh) { + #define gg ((GGraphObject *)gh) + coord_t i, xmin, ymin, xmax, ymax; + + if (gh->vmt != &graphVMT || !_gwinDrawStart(gh)) + return; + + xmin = -gg->xorigin; + xmax = gh->width-gg->xorigin-1; + ymin = -gg->yorigin; + ymax = gh->height-gg->yorigin-1; + + // x grid - this code assumes that the GGraphGridStyle is a superset of GGraphListStyle + if (gg->style.xgrid.type != GGRAPH_LINE_NONE && gg->style.xgrid.spacing >= 2) { + for(i = gg->style.xgrid.spacing; i <= xmax; i += gg->style.xgrid.spacing) + lineto(gg, i, ymin, i, ymax, (GGraphLineStyle *)&gg->style.xgrid); + for(i = -gg->style.xgrid.spacing; i >= xmin; i -= gg->style.xgrid.spacing) + lineto(gg, i, ymin, i, ymax, (GGraphLineStyle *)&gg->style.xgrid); + } + + // y grid - this code assumes that the GGraphGridStyle is a superset of GGraphListStyle + if (gg->style.ygrid.type != GGRAPH_LINE_NONE && gg->style.ygrid.spacing >= 2) { + for(i = gg->style.ygrid.spacing; i <= ymax; i += gg->style.ygrid.spacing) + lineto(gg, xmin, i, xmax, i, (GGraphLineStyle *)&gg->style.ygrid); + for(i = -gg->style.ygrid.spacing; i >= ymin; i -= gg->style.ygrid.spacing) + lineto(gg, xmin, i, xmax, i, (GGraphLineStyle *)&gg->style.ygrid); + } + + // x axis + lineto(gg, xmin, 0, xmax, 0, &gg->style.xaxis); + if ((gg->style.flags & GWIN_GRAPH_STYLE_XAXIS_NEGATIVE_ARROWS)) { + if (xmin > 0 || xmin < -(GGRAPH_ARROW_SIZE+1)) { + lineto(gg, xmin, 0, xmin+GGRAPH_ARROW_SIZE, GGRAPH_ARROW_SIZE, &gg->style.xaxis); + lineto(gg, xmin, 0, xmin+GGRAPH_ARROW_SIZE, -GGRAPH_ARROW_SIZE, &gg->style.xaxis); + } + } + if ((gg->style.flags & GWIN_GRAPH_STYLE_XAXIS_POSITIVE_ARROWS)) { + if (xmax < 0 || xmax > (GGRAPH_ARROW_SIZE+1)) { + lineto(gg, xmax, 0, xmax-GGRAPH_ARROW_SIZE, GGRAPH_ARROW_SIZE, &gg->style.xaxis); + lineto(gg, xmax, 0, xmax-GGRAPH_ARROW_SIZE, -GGRAPH_ARROW_SIZE, &gg->style.xaxis); + } + } + + // y axis + lineto(gg, 0, ymin, 0, ymax, &gg->style.yaxis); + if ((gg->style.flags & GWIN_GRAPH_STYLE_YAXIS_NEGATIVE_ARROWS)) { + if (ymin > 0 || ymin < -(GGRAPH_ARROW_SIZE+1)) { + lineto(gg, 0, ymin, GGRAPH_ARROW_SIZE, ymin+GGRAPH_ARROW_SIZE, &gg->style.yaxis); + lineto(gg, 0, ymin, -GGRAPH_ARROW_SIZE, ymin+GGRAPH_ARROW_SIZE, &gg->style.yaxis); + } + } + if ((gg->style.flags & GWIN_GRAPH_STYLE_YAXIS_POSITIVE_ARROWS)) { + if (ymax < 0 || ymax > (GGRAPH_ARROW_SIZE+1)) { + lineto(gg, 0, ymax, GGRAPH_ARROW_SIZE, ymax-GGRAPH_ARROW_SIZE, &gg->style.yaxis); + lineto(gg, 0, ymax, -GGRAPH_ARROW_SIZE, ymax-GGRAPH_ARROW_SIZE, &gg->style.yaxis); + } + } + + _gwinDrawEnd(gh); + #undef gg +} + +void gwinGraphStartSet(GHandle gh) { + if (gh->vmt != &graphVMT) + return; + + gh->flags &= ~GGRAPH_FLG_CONNECTPOINTS; +} + +void gwinGraphDrawPoint(GHandle gh, coord_t x, coord_t y) { + #define gg ((GGraphObject *)gh) + + if (gh->vmt != &graphVMT || !_gwinDrawStart(gh)) + return; + + if ((gh->flags & GGRAPH_FLG_CONNECTPOINTS)) { + // Draw the line + lineto(gg, gg->lastx, gg->lasty, x, y, &gg->style.line); + + // Redraw the previous point because the line may have overwritten it + pointto(gg, gg->lastx, gg->lasty, &gg->style.point); + + } else + gh->flags |= GGRAPH_FLG_CONNECTPOINTS; + + // Save this point for next time. + gg->lastx = x; + gg->lasty = y; + + // Draw this point. + pointto(gg, x, y, &gg->style.point); + + _gwinDrawEnd(gh); + #undef gg +} + +void gwinGraphDrawPoints(GHandle gh, const point *points, unsigned count) { + #define gg ((GGraphObject *)gh) + unsigned i; + const point *p; + + if (gh->vmt != &graphVMT || !_gwinDrawStart(gh)) + return; + + // Draw the connecting lines + for(p = points, i = 0; i < count; p++, i++) { + if ((gh->flags & GGRAPH_FLG_CONNECTPOINTS)) { + // Draw the line + lineto(gg, gg->lastx, gg->lasty, p->x, p->y, &gg->style.line); + + // Redraw the previous point because the line may have overwritten it + if (i == 0) + pointto(gg, gg->lastx, gg->lasty, &gg->style.point); + + } else + gh->flags |= GGRAPH_FLG_CONNECTPOINTS; + + // Save this point for next time. + gg->lastx = p->x; + gg->lasty = p->y; + } + + + // Draw the points. + for(p = points, i = 0; i < count; p++, i++) + pointto(gg, p->x, p->y, &gg->style.point); + + _gwinDrawEnd(gh); + #undef gg +} + +#endif /* GFX_USE_GWIN && GWIN_NEED_GRAPH */ diff --git a/src/gwin/gwin_graph.h b/src/gwin/gwin_graph.h new file mode 100644 index 00000000..eea80679 --- /dev/null +++ b/src/gwin/gwin_graph.h @@ -0,0 +1,186 @@ +/* + * 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_graph.h + * @brief GWIN GRAPH module header file + * + * @defgroup Graph Graph + * @ingroup Windows + * + * @details GWIN allows it to easily draw graphs + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_GRAPH must be set to TRUE in your gfxconf.h + * + * @{ + */ + +#ifndef _GWIN_GRAPH_H +#define _GWIN_GRAPH_H + +/* This file is included within "src/gwin/sys_defs.h" */ + +typedef enum GGraphPointType_e { + GGRAPH_POINT_NONE, GGRAPH_POINT_DOT, GGRAPH_POINT_SQUARE, GGRAPH_POINT_CIRCLE + } GGraphPointType; + +typedef struct GGraphPointStyle_t { + GGraphPointType type; + coord_t size; + color_t color; + } GGraphPointStyle; + +typedef enum GGraphLineType_e { + GGRAPH_LINE_NONE, GGRAPH_LINE_SOLID, GGRAPH_LINE_DOT, GGRAPH_LINE_DASH + } GGraphLineType; + +typedef struct GGraphLineStyle_t { + GGraphLineType type; + coord_t size; + color_t color; + } GGraphLineStyle; + +typedef struct GGraphGridStyle_t { + GGraphLineType type; + coord_t size; + color_t color; + coord_t spacing; + } GGraphGridStyle; + +typedef struct GGraphStyle_t { + GGraphPointStyle point; + GGraphLineStyle line; + GGraphLineStyle xaxis; + GGraphLineStyle yaxis; + GGraphGridStyle xgrid; + GGraphGridStyle ygrid; + uint16_t flags; + #define GWIN_GRAPH_STYLE_XAXIS_POSITIVE_ARROWS 0x0001 + #define GWIN_GRAPH_STYLE_XAXIS_NEGATIVE_ARROWS 0x0002 + #define GWIN_GRAPH_STYLE_YAXIS_POSITIVE_ARROWS 0x0004 + #define GWIN_GRAPH_STYLE_YAXIS_NEGATIVE_ARROWS 0x0008 + #define GWIN_GRAPH_STYLE_POSITIVE_AXIS_ARROWS (GWIN_GRAPH_STYLE_XAXIS_POSITIVE_ARROWS|GWIN_GRAPH_STYLE_YAXIS_POSITIVE_ARROWS) + #define GWIN_GRAPH_STYLE_NEGATIVE_AXIS_ARROWS (GWIN_GRAPH_STYLE_XAXIS_NEGATIVE_ARROWS|GWIN_GRAPH_STYLE_YAXIS_NEGATIVE_ARROWS) + #define GWIN_GRAPH_STYLE_XAXIS_ARROWS (GWIN_GRAPH_STYLE_XAXIS_POSITIVE_ARROWS|GWIN_GRAPH_STYLE_XAXIS_NEGATIVE_ARROWS) + #define GWIN_GRAPH_STYLE_YAXIS_ARROWS (GWIN_GRAPH_STYLE_YAXIS_POSITIVE_ARROWS|GWIN_GRAPH_STYLE_YAXIS_NEGATIVE_ARROWS) + #define GWIN_GRAPH_STYLE_ALL_AXIS_ARROWS (GWIN_GRAPH_STYLE_XAXIS_ARROWS|GWIN_GRAPH_STYLE_YAXIS_ARROWS) +} GGraphStyle; + +// A graph window +typedef struct GGraphObject { + GWindowObject g; + GGraphStyle style; + coord_t xorigin, yorigin; + coord_t lastx, lasty; + } GGraphObject; + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a graph 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] gg The GGraphObject structure to initialise. If this is NULL the structure is dynamically allocated. + * @param[in] pInit The initialization parameters to use + * + * @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 The dimensions and position may be changed to fit on the real screen. + * @note A graph does not save the drawing state. It is not automatically redrawn if the window is moved or + * its visibility state is changed. + * @note The coordinate system within the window for graphing operations (but not for any other drawing + * operation) is relative to the bottom left corner and then shifted right and up by the specified + * graphing x and y origin. Note that this system is inverted in the y direction relative to the display. + * This gives the best graphing arrangement ie. increasing y values are closer to the top of the display. + * + * @api + */ +GHandle gwinGGraphCreate(GDisplay *g, GGraphObject *gg, const GWindowInit *pInit); +#define gwinGraphCreate(gg, pInit) gwinGGraphCreate(GDISP, gg, pInit) + +/** + * @brief Set the style of the graphing operations. + * + * @param[in] gh The window handle (must be a graph window) + * @param[in] pstyle The graph style to set. + * @note The graph is not automatically redrawn. The new style will apply to any new drawing operations. + * + * @api + */ +void gwinGraphSetStyle(GHandle gh, const GGraphStyle *pstyle); + +/** + * @brief Set the origin for graphing operations. + * + * @param[in] gh The window handle (must be a graph window) + * @param[in] x, y The new origin for the graph (in graph coordinates relative to the bottom left corner). + * @note The graph is not automatically redrawn. The new origin will apply to any new drawing operations. + * + * @api + */ +void gwinGraphSetOrigin(GHandle gh, coord_t x, coord_t y); + +/** + * @brief Draw the axis and the background grid. + * + * @param[in] gh The window handle (must be a graph window) + * @note The graph is not automatically cleared. You must do that first by calling gwinClear(). + * + * @api + */ +void gwinGraphDrawAxis(GHandle gh); + +/** + * @brief Start a new set of graphing data. + * @details This prevents a line being drawn from the last data point to the next point to be drawn. + * + * @param[in] gh The window handle (must be a graph window) + * + * @api + */ +void gwinGraphStartSet(GHandle gh); + +/** + * @brief Draw a graph point. + * @details A graph point and a line connecting to the previous point will be drawn. + * + * @param[in] gh The window handle (must be a graph window) + * @param[in] x, y The new point for the graph. + * + * @api + */ +void gwinGraphDrawPoint(GHandle gh, coord_t x, coord_t y); + +/** + * @brief Draw multiple graph points. + * @details A graph point and a line connecting to each previous point will be drawn. + * + * @param[in] gh The window handle (must be a graph window) + * @param[in] points The array of points for the graph. + * @param[in] count The number of points in the array. + * @note This is slightly more efficient than calling gwinGraphDrawPoint() repeatedly. + * + * @api + */ +void gwinGraphDrawPoints(GHandle gh, const point *points, unsigned count); + +#ifdef __cplusplus +} +#endif + +#endif /* _GWIN_GRAPH_H */ +/** @} */ + diff --git a/src/gwin/gwin_gwin.c b/src/gwin/gwin_gwin.c new file mode 100644 index 00000000..a4cef125 --- /dev/null +++ b/src/gwin/gwin_gwin.c @@ -0,0 +1,380 @@ +/* + * 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" + +/*----------------------------------------------- + * 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 + + 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_PIXELREAD + color_t gwinGetPixelColor(GHandle gh, coord_t x, coord_t y) { + if (!_gwinDrawStart(gh)) return; + 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.c b/src/gwin/gwin_image.c new file mode 100644 index 00000000..370d7bdd --- /dev/null +++ b/src/gwin/gwin_image.c @@ -0,0 +1,162 @@ +/* + * 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_image.c + * @brief GWIN sub-system image code + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_IMAGE + +#include "gwin_class.h" + +#define widget(gh) ((GImageObject *)gh) + +static void _destroy(GWindowObject *gh) { + if (gdispImageIsOpen(&widget(gh)->image)) + gdispImageClose(&widget(gh)->image); +} + +#if GWIN_NEED_IMAGE_ANIMATION + static void _timer(void *param) { + _gwinUpdate((GHandle)param); + } +#endif + +static void _redraw(GHandle gh) { + coord_t x, y, w, h, dx, dy; + color_t bg; + #if GWIN_NEED_IMAGE_ANIMATION + delaytime_t delay; + #endif + + // The default display area + dx = 0; + dy = 0; + x = gh->x; + y = gh->y; + w = gh->width; + h = gh->height; + bg = gwinGetDefaultBgColor(); + + // If the image isn't open just clear the area + if (!gdispImageIsOpen(&widget(gh)->image)) { + gdispGFillArea(gh->display, x, y, w, h, bg); + return; + } + + // Center horizontally if the area is larger than the image + if (widget(gh)->image.width < w) { + w = widget(gh)->image.width; + dx = (gh->width-w)/2; + x += dx; + if (dx) + gdispGFillArea(gh->display, gh->x, y, dx, h, bg); + gdispGFillArea(gh->display, x+w, y, gh->width-dx-w, h, bg); + dx = 0; + } + + // Center image horizontally if the area is smaller than the image + else if (widget(gh)->image.width > w) { + dx = (widget(gh)->image.width - w)/2; + } + + // Center vertically if the area is larger than the image + if (widget(gh)->image.height < h) { + h = widget(gh)->image.height; + dy = (gh->height-h)/2; + y += dy; + if (dy) + gdispGFillArea(gh->display, x, gh->y, w, dy, bg); + gdispGFillArea(gh->display, x, y+h, w, gh->height-dy-h, bg); + dy = 0; + } + + // Center image vertically if the area is smaller than the image + else if (widget(gh)->image.height > h) { + dy = (widget(gh)->image.height - h)/2; + } + + // Reset the background color in case it has changed + gdispImageSetBgColor(&widget(gh)->image, bg); + + // Display the image + gdispGImageDraw(gh->display, &widget(gh)->image, x, y, w, h, dx, dy); + + #if GWIN_NEED_IMAGE_ANIMATION + // read the delay for the next frame + delay = gdispImageNext(&widget((GHandle)gh)->image); + + // Wait for that delay if required + switch(delay) { + case TIME_INFINITE: + // Everything is done + break; + case TIME_IMMEDIATE: + // We can't allow a continuous loop here as it would lock the system up so we delay for the minimum period + delay = 1; + // Fall through + default: + // Start the timer to draw the next frame of the animation + gtimerStart(&widget((GHandle)gh)->timer, _timer, (void*)gh, FALSE, delay); + break; + } + #endif +} + +static const gwinVMT imageVMT = { + "Image", // The class name + sizeof(GImageObject), // The object size + _destroy, // The destroy routine + _redraw, // The redraw routine + 0, // The after-clear routine +}; + +GHandle gwinGImageCreate(GDisplay *g, GImageObject *gobj, GWindowInit *pInit) { + if (!(gobj = (GImageObject *)_gwindowCreate(g, &gobj->g, pInit, &imageVMT, 0))) + return 0; + + // Ensure the gdispImageIsOpen() gives valid results + gdispImageInit(&gobj->image); + + // Initialise the timer + #if GWIN_NEED_IMAGE_ANIMATION + gtimerInit(&gobj->timer); + #endif + + gwinSetVisible((GHandle)gobj, pInit->show); + + return (GHandle)gobj; +} + +bool_t gwinImageOpenGFile(GHandle gh, GFILE *f) { + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&imageVMT) + return FALSE; + + if (gdispImageIsOpen(&widget(gh)->image)) + gdispImageClose(&widget(gh)->image); + + if ((gdispImageOpenGFile(&widget(gh)->image, f) & GDISP_IMAGE_ERR_UNRECOVERABLE)) + return FALSE; + + _gwinUpdate(gh); + + return TRUE; +} + +gdispImageError gwinImageCache(GHandle gh) { + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&imageVMT) + return GDISP_IMAGE_ERR_BADFORMAT; + + return gdispImageCache(&widget(gh)->image); +} + +#endif // GFX_USE_GWIN && GWIN_NEED_IMAGE diff --git a/src/gwin/gwin_image.h b/src/gwin/gwin_image.h new file mode 100644 index 00000000..2e0a3218 --- /dev/null +++ b/src/gwin/gwin_image.h @@ -0,0 +1,127 @@ +/* + * 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_image.h + * @brief GWIN image widget header file. + * + * @defgroup ImageBox ImageBox + * @ingroup Widgets + * + * @details GWIN allos it to create an image widget. The widget + * takes no user input. + * + * @pre GFX_USE_GDISP must be set to TRUE in your gfxconf.h + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GDISP_NEED_IMAGE must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_IMAGE must be set to TRUE in your gfxconf.h + * @pre At least one image type must be enabled in your gfxconf.h + * + * @{ + */ + +#ifndef _GWIN_IMAGE_H +#define _GWIN_IMAGE_H + +// This file is included within "src/gwin/sys_defs.h" + +// An image window +typedef struct GImageObject { + GWindowObject g; + gdispImage image; // The image itself + #if GWIN_NEED_IMAGE_ANIMATION + GTimer timer; // Timer used for animated images + #endif +} GImageObject; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create an image widget. + * @details Display's a picture. + * @return NULL if there is no resultant drawing area, otherwise the widget handle. + * + * @param[in] g The GDisplay to display this window on + * @param[in] widget The image widget structure to initialise. If this is NULL, the structure is dynamically allocated. + * @param[in] pInit The initialization parameters to use. + * + * @note The default background color gets set to the current default one. + * @note An image window knows how to redraw. + * + * @api + */ +GHandle gwinGImageCreate(GDisplay *g, GImageObject *widget, GWindowInit *pInit); +#define gwinImageCreate(w, pInit) gwinGImageCreate(GDISP, w, pInit) + +/** + * @brief Opens the image using a GFILE + * @return TRUE if the image can be opened + * + * @param[in] gh The widget (must be an image widget) + * @param[in] f The open (for reading) GFILE to use + * + * @api + */ +bool_t gwinImageOpenGFile(GHandle gh, GFILE *f); + +/** + * @brief Opens the image using the specified filename + * @return TRUE if the open succeeds + * + * @param[in] gh The widget (must be an image widget) + * @param[in] filename The filename to open + * + * @api + */ +#define gwinImageOpenFile(gh, filename) gwinImageOpenGFile((gh), gfileOpen((filename), "rb")) + + /** + * @brief Sets the input routines that support reading the image from memory + * in RAM or flash. + * @pre GFILE_NEED_MEMFS must be TRUE + * @return TRUE if the IO open function succeeds + * + * @param[in] gh The widget (must be an image widget) + * @param[in] ptr A pointer to the image in RAM or Flash + * + * @api + */ +#define gwinImageOpenMemory(gh, ptr) gwinImageOpenGFile((gh), gfileOpenMemory((void *)(ptr), "rb")) + +/** + * @brief Sets the input routines that support reading the image from a BaseFileStream (eg. an SD-Card). + * @return TRUE if the IO open function succeeds + * @pre GFILE_NEED_CHIBIOSFS and GFX_USE_OS_CHIBIOS must be TRUE + * + * @param[in] gh The widget (must be an image widget) + * @param[in] streamPtr A pointer to the (open) BaseFileStream object. + * + * @api + */ +#define gwinImageOpenStream(gh, streamPtr) gwinImageOpenGFile((gh), gfileOpenBaseFIleStream((streamPtr), "rb")) + +/** + * @brief Cache the image. + * @details Decodes and caches the current frame into RAM. + * + * @param[in] gh The widget (must be an image widget) + * + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @api + */ +gdispImageError gwinImageCache(GHandle gh); + +#ifdef __cplusplus +} +#endif + +#endif // _GWIN_IMAGE_H +/** @} */ + diff --git a/src/gwin/gwin_label.c b/src/gwin/gwin_label.c new file mode 100644 index 00000000..3be9d0a9 --- /dev/null +++ b/src/gwin/gwin_label.c @@ -0,0 +1,168 @@ +/* + * 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_label.c + * @brief GWIN label widget header file + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_LABEL + +#include "gwin_class.h" + +// macros to assist in data type conversions +#define gh2obj ((GLabelObject *)gh) +#define gw2obj ((GLabelObject *)gw) + +// flags for the GLabelObject +#define GLABEL_FLG_WAUTO (GWIN_FIRST_CONTROL_FLAG << 0) +#define GLABEL_FLG_HAUTO (GWIN_FIRST_CONTROL_FLAG << 1) +#define GLABEL_FLG_BORDER (GWIN_FIRST_CONTROL_FLAG << 2) + +// simple: single line with no wrapping +static coord_t getwidth(const char *text, font_t font, coord_t maxwidth) { + (void) maxwidth; + + return gdispGetStringWidth(text, font)+2; // Allow one pixel of padding on each side +} + +// simple: single line with no wrapping +static coord_t getheight(const char *text, font_t font, coord_t maxwidth) { + (void) text; + (void) maxwidth; + + return gdispGetFontMetric(font, fontHeight); +} + +static void gwinLabelDefaultDraw(GWidgetObject *gw, void *param); + +static const gwidgetVMT labelVMT = { + { + "Label", // The class name + sizeof(GLabelObject), // The object size + _gwidgetDestroy, // The destroy routine + _gwidgetRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinLabelDefaultDraw, // default drawing routine + #if GINPUT_NEED_MOUSE + { + 0, // Process mose down events (NOT USED) + 0, // Process mouse up events (NOT USED) + 0, // Process mouse move events (NOT USED) + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 0, // No toggle role + 0, // Assign Toggles (NOT USED) + 0, // Get Toggles (NOT USED) + 0, // Process toggle off event (NOT USED) + 0, // Process toggle on event (NOT USED) + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, // No dial roles + 0, // Assign Dials (NOT USED) + 0, // Get Dials (NOT USED) + 0, // Procees dial move events (NOT USED) + }, + #endif +}; + +GHandle gwinGLabelCreate(GDisplay *g, GLabelObject *widget, GWidgetInit *pInit) { + uint16_t flags = 0; + + // auto assign width + if (pInit->g.width <= 0) { + + flags |= GLABEL_FLG_WAUTO; + pInit->g.width = getwidth(pInit->text, gwinGetDefaultFont(), gdispGGetWidth(g) - pInit->g.x); + } + + // auto assign height + if (pInit->g.height <= 0) { + flags |= GLABEL_FLG_HAUTO; + pInit->g.height = getheight(pInit->text, gwinGetDefaultFont(), gdispGGetWidth(g) - pInit->g.x); + } + + if (!(widget = (GLabelObject *)_gwidgetCreate(g, &widget->w, pInit, &labelVMT))) + return 0; + + #if GWIN_LABEL_ATTRIBUTE + widget->tab = 0; + widget->attr = 0; + #endif + + widget->w.g.flags |= flags; + gwinSetVisible(&widget->w.g, pInit->g.show); + + return (GHandle)widget; +} + +void gwinLabelSetBorder(GHandle gh, bool_t border) { + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&labelVMT) + return; + + if (border) + gh2obj->w.g.flags |= GLABEL_FLG_BORDER; + else + gh2obj->w.g.flags &=~ GLABEL_FLG_BORDER; +} + +#if GWIN_LABEL_ATTRIBUTE + void gwinLabelSetAttribute(GHandle gh, coord_t tab, const char* attr) { + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&labelVMT) + return; + + gh2obj->tab = tab; + gh2obj->attr = attr; + + gwinRedraw(gh); + } +#endif // GWIN_LABEL_ATTRIBUTE + +static void gwinLabelDefaultDraw(GWidgetObject *gw, void *param) { + coord_t w, h; + color_t c; + (void) param; + + // is it a valid handle? + if (gw->g.vmt != (gwinVMT *)&labelVMT) + return; + + w = (gw->g.flags & GLABEL_FLG_WAUTO) ? getwidth(gw->text, gw->g.font, gdispGGetWidth(gw->g.display) - gw->g.x) : gw->g.width; + h = (gw->g.flags & GLABEL_FLG_HAUTO) ? getheight(gw->text, gw->g.font, gdispGGetWidth(gw->g.display) - gw->g.x) : gw->g.height; + c = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text; + + if (gw->g.width != w || gw->g.height != h) { + gwinResize(&gw->g, w, h); + + return; + } + + #if GWIN_LABEL_ATTRIBUTE + if (gw2obj->attr) { + gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw2obj->tab, h, gw2obj->attr, gw->g.font, c, gw->pstyle->background, justifyLeft); + gdispGFillStringBox(gw->g.display, gw->g.x + gw2obj->tab, gw->g.y, w-gw2obj->tab, h, gw->text, gw->g.font, c, gw->pstyle->background, justifyLeft); + } else + gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, w, h, gw->text, gw->g.font, c, gw->pstyle->background, justifyLeft); + #else + gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, w, h, gw->text, gw->g.font, c, gw->pstyle->background, justifyLeft); + #endif + + // render the border (if any) + if (gw->g.flags & GLABEL_FLG_BORDER) + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, w, h, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); +} + +#endif // GFX_USE_GWIN && GFX_NEED_LABEL diff --git a/src/gwin/gwin_label.h b/src/gwin/gwin_label.h new file mode 100644 index 00000000..b5c45589 --- /dev/null +++ b/src/gwin/gwin_label.h @@ -0,0 +1,104 @@ +/* + * 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_label.h + * @brief GWIN label widget header file + * + * @defgroup Label Label + * @ingroup Widgets + * + * @details GWIN allos it to create an label widget. The widget + * takes no user input. + * + * @pre GFX_USE_GDISP must be set to TRUE in your gfxconf.h + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GDISP_NEED_TEXT must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_LABEL must be set to TRUE in your gfxconf.h + * @pre The fonts you want to use must be enabled in your gfxconf.h + * + * @{ + */ + +#ifndef _GWIN_LABEL_H +#define _GWIN_LABEL_H + +// This file is included within "src/gwin/gwin_widget.h" + +// An label window +typedef struct GLabelObject { + GWidgetObject w; + + #if GWIN_LABEL_ATTRIBUTE + coord_t tab; + const char* attr; + #endif +} GLabelObject; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a label widget. + * @details A label widget is a simple window which has a static text. + * + * @param[in] g The GDisplay to display this window on + * @param[in] widget The label structure to initialise. If this is NULL, the structure is dynamically allocated. + * @param[in] pInit The initialisation parameters to use. + * + * @return NULL if there is no resultat drawing area, otherwise the widget handle. + * + * @api + */ +GHandle gwinGLabelCreate(GDisplay *g, GLabelObject *widget, GWidgetInit *pInit); +#define gwinLabelCreate(w, pInit) gwinGLabelCreate(GDISP, w, pInit) + +/** + * @brief Border settings for the default rendering routine + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] border Shall a border be rendered? + * + * @api + */ +void gwinLabelSetBorder(GHandle gh, bool_t border); + +#if GWIN_LABEL_ATTRIBUTE || defined(__DOXYGEN__) + /** + * @brief Add an text attribute in front of the normal label text + * @details Often you want to display a text like this: + * Current IP: 192.168.1.42 + * In that case, the actual IP will be variable, the text in front of it + * always remains the same. The static text is called the attribute and can be + * set using this function. + * Furthermore, the tab can be set in order to vertically align multiple labels. + * Please check out the website for further explanation, illustraions and usage + * examples. + * + * @note The attribute text is not copied into private memory and so it + * must be a constant string, not one allocated in a stack buffer. + * @note Use of this construct is discouraged. The appropriate way is to + * create two labels - one for the static text and one for the + * dynamic text. + * + * @param[in] gh The widget handle (must be a label handle) + * @param[in] tab The distance of the label text from the left widget edge + * @param[in] attr The attribute to be displayed + * + * @api + */ + void gwinLabelSetAttribute(GHandle gh, coord_t tab, const char* attr); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // _GWIN_LABEL_H +/** @} */ + diff --git a/src/gwin/gwin_list.c b/src/gwin/gwin_list.c new file mode 100644 index 00000000..265053e1 --- /dev/null +++ b/src/gwin/gwin_list.c @@ -0,0 +1,696 @@ +/* + * 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_list.c + * @brief GWIN list widget header file + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_LIST + +#include "gwin_class.h" +#include +#include + +// user for the default drawing routine +#define SCROLLWIDTH 16 // the border from the scroll buttons to the frame +#define ARROW 10 // arrow side length +#define HORIZONTAL_PADDING 5 // extra horizontal padding for text +#define VERTICAL_PADDING 2 // extra vertical padding for text + +// Macro's to assist in data type conversions +#define gh2obj ((GListObject *)gh) +#define gw2obj ((GListObject *)gw) +#define qi2li ((ListItem *)qi) +#define qix2li ((ListItem *)qix) +#define ple ((GEventGWinList *)pe) + +// Flags for the GListObject +#define GLIST_FLG_MULTISELECT (GWIN_FIRST_CONTROL_FLAG << 0) +#define GLIST_FLG_HASIMAGES (GWIN_FIRST_CONTROL_FLAG << 1) +#define GLIST_FLG_SCROLLALWAYS (GWIN_FIRST_CONTROL_FLAG << 2) +#define GLIST_FLG_SCROLLSMOOTH (GWIN_FIRST_CONTROL_FLAG << 3) +#define GLIST_FLG_ENABLERENDER (GWIN_FIRST_CONTROL_FLAG << 4) + +// Flags on a ListItem. +#define GLIST_FLG_SELECTED 0x0001 + +typedef struct ListItem { + gfxQueueASyncItem q_item; // This must be the first member in the struct + + uint16_t flags; + uint16_t param; // A parameter the user can specify himself + const char* text; + #if GWIN_NEED_LIST_IMAGES + gdispImage* pimg; + #endif +} ListItem; + +static void sendListEvent(GWidgetObject *gw, int item) { + GSourceListener* psl; + GEvent* pe; + + // Trigger a GWIN list event + psl = 0; + + while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { + if (!(pe = geventGetEventBuffer(psl))) + continue; + + ple->type = GEVENT_GWIN_LIST; + ple->gwin = (GHandle)gw; + ple->item = item; + #if GWIN_WIDGET_TAGS + ple->tag = gw->tag; + #endif + + geventSendEvent(psl); + } +} + +static void gwinListDefaultDraw(GWidgetObject* gw, void* param); + +#if GINPUT_NEED_MOUSE + static void MouseSelect(GWidgetObject* gw, coord_t x, coord_t y) { + const gfxQueueASyncItem* qi; + int item, i; + coord_t iheight; + (void) x; + + iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING; + + // Handle click over the list area + item = (gw2obj->top + y) / iheight; + + if (item < 0 || item >= gw2obj->cnt) + return; + + for(qi = gfxQueueASyncPeek(&gw2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { + if ((gw->g.flags & GLIST_FLG_MULTISELECT)) { + if (item == i) { + qi2li->flags ^= GLIST_FLG_SELECTED; + break; + } + } else { + if (item == i) + qi2li->flags |= GLIST_FLG_SELECTED; + else + qi2li->flags &=~ GLIST_FLG_SELECTED; + } + } + + _gwinUpdate(&gw->g); + sendListEvent(gw, item); + + } + + // a mouse down has occurred over the list area + static void MouseDown(GWidgetObject* gw, coord_t x, coord_t y) { + coord_t iheight, pgsz; + + // Save our mouse start position + gw2obj->start_mouse_x = x; + gw2obj->start_mouse_y = y; + gw2obj->last_mouse_y = y; + + // For smooth scrolling, scrolling is done in the MouseMove and selection is done on MouseUp + if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH) + return; + + // Some initial stuff + iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING; + pgsz = gw->g.height-2; + + // Handle click over the scroll bar + if (x >= gw->g.width-(SCROLLWIDTH+2) && (gw2obj->cnt > pgsz/iheight || (gw->g.flags & GLIST_FLG_SCROLLALWAYS))) { + if (y < 2*ARROW) { + if (gw2obj->top > 0) { + gw2obj->top -= iheight; + if (gw2obj->top < 0) + gw2obj->top = 0; + _gwinUpdate(&gw->g); + } + } else if (y >= gw->g.height - 2*ARROW) { + if (gw2obj->top < gw2obj->cnt * iheight - pgsz) { + gw2obj->top += iheight; + if (gw2obj->top > gw2obj->cnt * iheight - pgsz) + gw2obj->top = gw2obj->cnt * iheight - pgsz; + _gwinUpdate(&gw->g); + } + } else if (y < gw->g.height/2) { + if (gw2obj->top > 0) { + if (gw2obj->top > pgsz) + gw2obj->top -= pgsz; + else + gw2obj->top = 0; + _gwinUpdate(&gw->g); + } + } else { + if (gw2obj->top < gw2obj->cnt * iheight - pgsz) { + if (gw2obj->top < gw2obj->cnt * iheight - 2*pgsz) + gw2obj->top += pgsz; + else + gw2obj->top = gw2obj->cnt * iheight - pgsz; + _gwinUpdate(&gw->g); + } + } + return; + } + + MouseSelect(gw, x, y); + } + + static void MouseUp(GWidgetObject* gw, coord_t x, coord_t y) { + // Only act when we are a smooth scrolling list + if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH)) + return; + + // Only allow selection when we did not scroll + if (abs(gw2obj->start_mouse_x - x) > 4 || abs(gw2obj->start_mouse_y - y) > 4) + return; + + MouseSelect(gw, x, y); + } + + static void MouseMove(GWidgetObject* gw, coord_t x, coord_t y) { + int iheight, oldtop; + (void) x; + + if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH)) return; + + if (gw2obj->last_mouse_y != y) { + oldtop = gw2obj->top; + iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING; + + gw2obj->top -= y - gw2obj->last_mouse_y; + if (gw2obj->top >= gw2obj->cnt * iheight - (gw->g.height-2)) + gw2obj->top = gw2obj->cnt * iheight - (gw->g.height-2) - 1; + if (gw2obj->top < 0) + gw2obj->top = 0; + gw2obj->last_mouse_y = y; + if (oldtop != gw2obj->top) + _gwinUpdate(&gw->g); + } + } +#endif + +#if GINPUT_NEED_TOGGLE + // a toggle-on has occurred + static void ToggleOn(GWidgetObject *gw, uint16_t role) { + const gfxQueueASyncItem * qi; + const gfxQueueASyncItem * qix; + int i; + + switch (role) { + // select down + case 0: + for (i = 0, qi = gfxQueueASyncPeek(&gw2obj->list_head); qi; qi = gfxQueueASyncNext(qi), i++) { + if ((qi2li->flags & GLIST_FLG_SELECTED)) { + qix = gfxQueueASyncNext(qi); + if (qix) { + qi2li->flags &=~ GLIST_FLG_SELECTED; + qix2li->flags |= GLIST_FLG_SELECTED; + _gwinUpdate(&gw->g); + } + break; + } + } + break; + + // select up + case 1: + qi = gfxQueueASyncPeek(&gw2obj->list_head); + qix = 0; + + for (i = 0; qi; qix = qi, qi = gfxQueueASyncNext(qi), i++) { + if ((qi2li->flags & GLIST_FLG_SELECTED)) + if (qix) { + qi2li->flags &=~ GLIST_FLG_SELECTED; + qix2li->flags |= GLIST_FLG_SELECTED; + _gwinUpdate(&gw->g); + } + break; + } + } + break; + } + } + + static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { + if (role) + gw2obj->t_up = instance; + else + gw2obj->t_dn = instance; + } + + static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { + return role ? gw2obj->t_up : gw2obj->t_dn; + } +#endif + +static void _destroy(GHandle gh) { + const gfxQueueASyncItem* qi; + + while((qi = gfxQueueASyncGet(&gh2obj->list_head))) + gfxFree((void *)qi); + + _gwidgetDestroy(gh); +} + +static const gwidgetVMT listVMT = { + { + "List", // The class name + sizeof(GListObject), // The object size + _destroy, // The destroy routine + _gwidgetRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinListDefaultDraw, // default drawing routine + #if GINPUT_NEED_MOUSE + { + MouseDown, + MouseUp, + MouseMove, + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 2, // two toggle roles + ToggleAssign, // Assign toggles + ToggleGet, // get toggles + 0, + ToggleOn, // process toggle on event + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, + 0, + 0, + 0, + }, + #endif +}; + +GHandle gwinGListCreate(GDisplay *g, GListObject* gobj, GWidgetInit* pInit, bool_t multiselect) { + if (!(gobj = (GListObject *)_gwidgetCreate(g, &gobj->w, pInit, &listVMT))) + return 0; + + // initialize the item queue + gfxQueueASyncInit(&gobj->list_head); + gobj->cnt = 0; + gobj->top = 0; + if (multiselect) + gobj->w.g.flags |= GLIST_FLG_MULTISELECT; + gobj->w.g.flags |= GLIST_FLG_SCROLLALWAYS; + gobj->w.g.flags |= GLIST_FLG_ENABLERENDER; + + gwinSetVisible(&gobj->w.g, pInit->g.show); + + return (GHandle)gobj; +} + +void gwinListEnableRender(GHandle gh, bool_t ena) { + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return; + + if (ena) { + gh->flags |= GLIST_FLG_ENABLERENDER; + gwinRedraw(gh); + } else { + gh->flags &=~ GLIST_FLG_ENABLERENDER; + } +} + +void gwinListSetScroll(GHandle gh, scroll_t flag) { + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return; + + ((GListObject*)gh)->w.g.flags &=~(GLIST_FLG_SCROLLSMOOTH | GLIST_FLG_SCROLLALWAYS); + switch (flag) { + case scrollAlways: + ((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLALWAYS; + break; + + case scrollAuto: + break; + + case scrollSmooth: + ((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLSMOOTH; + break; + } +} + +int gwinListAddItem(GHandle gh, const char* item_name, bool_t useAlloc) { + ListItem *newItem; + + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return -1; + + if (useAlloc) { + size_t len = strlen(item_name)+1; + if (!(newItem = gfxAlloc(sizeof(ListItem) + len))) + return -1; + + memcpy((char *)(newItem+1), item_name, len); + item_name = (const char *)(newItem+1); + } else { + if (!(newItem = gfxAlloc(sizeof(ListItem)))) + return -1; + } + + // the item is not selected when added + newItem->flags = 0; + newItem->param = 0; + newItem->text = item_name; + #if GWIN_NEED_LIST_IMAGES + newItem->pimg = 0; + #endif + + // select the item if it's the first in the list + if (gh2obj->cnt == 0 && !(gh->flags & GLIST_FLG_MULTISELECT)) + newItem->flags |= GLIST_FLG_SELECTED; + + // add the new item to the list + gfxQueueASyncPut(&gh2obj->list_head, &newItem->q_item); + + // increment the total amount of entries in the list widget + gh2obj->cnt++; + + _gwinUpdate(gh); + + // return the position in the list (-1 because we start with index 0) + return gh2obj->cnt-1; +} + +const char* gwinListItemGetText(GHandle gh, int item) { + const gfxQueueASyncItem* qi; + int i; + + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return 0; + + // watch out for an invalid item + if (item < 0 || item >= gh2obj->cnt) + return 0; + + for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { + if (i == item) + return qi2li->text; + } + return 0; +} + +int gwinListFindText(GHandle gh, const char* text) { + const gfxQueueASyncItem* qi; + int i; + + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return -1; + + // watch out for NULL pointers + if (!text) + return -1; + + for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { + if (strcmp(((ListItem *)qi)->text, text) == 0) + return i; + } + + return -1; +} + +int gwinListGetSelected(GHandle gh) { + const gfxQueueASyncItem * qi; + int i; + + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return -1; + + // Multi-select always returns -1. Use gwinListItemIsSelected() instead + if ((gh->flags & GLIST_FLG_MULTISELECT)) + return -1; + + for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { + if (qi2li->flags & GLIST_FLG_SELECTED) + return i; + } + + return -1; +} + +void gwinListItemSetParam(GHandle gh, int item, uint16_t param) { + const gfxQueueASyncItem * qi; + int i; + + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return; + + // watch out for an invalid item + if (item < 0 || item > (gh2obj->cnt) - 1) + return; + + for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { + if (i == item) { + qi2li->param = param; + break; + } + } +} + +void gwinListDeleteAll(GHandle gh) { + gfxQueueASyncItem* qi; + + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return; + + while((qi = gfxQueueASyncGet(&gh2obj->list_head))) + gfxFree(qi); + + gh->flags &= ~GLIST_FLG_HASIMAGES; + gh2obj->cnt = 0; + gh2obj->top = 0; + _gwinUpdate(gh); +} + +void gwinListItemDelete(GHandle gh, int item) { + const gfxQueueASyncItem * qi; + int i; + + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return; + + // watch out for an invalid item + if (item < 0 || item >= gh2obj->cnt) + return; + + for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { + if (i == item) { + gfxQueueASyncRemove(&gh2obj->list_head, (gfxQueueASyncItem*)qi); + gfxFree((void *)qi); + if (gh2obj->top >= item && gh2obj->top) + gh2obj->top--; + _gwinUpdate(gh); + break; + } + } +} + +uint16_t gwinListItemGetParam(GHandle gh, int item) { + const gfxQueueASyncItem * qi; + int i; + + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return 0; + + // watch out for an invalid item + if (item < 0 || item > (gh2obj->cnt) - 1) + return 0; + + for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { + if (i == item) + return qi2li->param; + } + return 0; +} + +bool_t gwinListItemIsSelected(GHandle gh, int item) { + const gfxQueueASyncItem * qi; + int i; + + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return FALSE; + + // watch out for an invalid item + if (item < 0 || item > (gh2obj->cnt) - 1) + return FALSE; + + for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { + if (i == item) + return (qi2li->flags & GLIST_FLG_SELECTED) ? TRUE : FALSE; + } + return FALSE; +} + +int gwinListItemCount(GHandle gh) { + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return 0; + + return gh2obj->cnt; +} + +const char* gwinListGetSelectedText(GHandle gh) { + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return 0; + + // return NULL if nothing is selected (or multi-select) + if (gwinListGetSelected(gh) < 0) + return 0; + + return gwinListItemGetText(gh, gwinListGetSelected(gh)); +} + +#if GWIN_NEED_LIST_IMAGES + void gwinListItemSetImage(GHandle gh, int item, gdispImage *pimg) { + const gfxQueueASyncItem * qi; + int i; + + // is it a valid handle? + if (gh->vmt != (gwinVMT *)&listVMT) + return; + + // watch out for an invalid item + if (item < 0 || item > (gh2obj->cnt) - 1) + return; + + for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { + if (i == item) { + qi2li->pimg = pimg; + if (pimg) + gh->flags |= GLIST_FLG_HASIMAGES; + break; + } + } + } +#endif + +static void gwinListDefaultDraw(GWidgetObject* gw, void* param) { + (void)param; + + #if GDISP_NEED_CONVEX_POLYGON + static const point upArrow[] = { {0, ARROW}, {ARROW, ARROW}, {ARROW/2, 0} }; + static const point downArrow[] = { {0, 0}, {ARROW, 0}, {ARROW/2, ARROW} }; + #endif + + const gfxQueueASyncItem* qi; + int i; + coord_t x, y, iheight, iwidth; + color_t fill; + const GColorSet * ps; + #if GWIN_NEED_LIST_IMAGES + coord_t sy; + #endif + + // is it a valid handle? + if (gw->g.vmt != (gwinVMT *)&listVMT) + return; + + // don't render if render has been disabled + if (!(gw->g.flags & GLIST_FLG_ENABLERENDER)) + return; + + ps = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled; + iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING; + x = 1; + + // the scroll area + if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH) { + iwidth = gw->g.width - 2 - 4; + if (gw2obj->cnt > 0) { + int max_scroll_value = gw2obj->cnt * iheight - gw->g.height-2; + if (max_scroll_value > 0) { + int bar_height = (gw->g.height-2) * (gw->g.height-2) / (gw2obj->cnt * iheight); + gdispGFillArea(gw->g.display, gw->g.x + gw->g.width-4, gw->g.y + 1, 2, gw->g.height-1, gw->pstyle->background); + gdispGFillArea(gw->g.display, gw->g.x + gw->g.width-4, gw->g.y + gw2obj->top * ((gw->g.height-2)-bar_height) / max_scroll_value, 2, bar_height, ps->edge); + } + } + } else if ((gw2obj->cnt > (gw->g.height-2) / iheight) || (gw->g.flags & GLIST_FLG_SCROLLALWAYS)) { + iwidth = gw->g.width - (SCROLLWIDTH+3); + gdispGFillArea(gw->g.display, gw->g.x+iwidth+2, gw->g.y+1, SCROLLWIDTH, gw->g.height-2, gdispBlendColor(ps->fill, gw->pstyle->background, 128)); + gdispGDrawLine(gw->g.display, gw->g.x+iwidth+1, gw->g.y+1, gw->g.x+iwidth+1, gw->g.y+gw->g.height-2, ps->edge); + #if GDISP_NEED_CONVEX_POLYGON + gdispGFillConvexPoly(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+(ARROW/2+1), upArrow, 3, ps->fill); + gdispGFillConvexPoly(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+gw->g.height-(ARROW+ARROW/2+1), downArrow, 3, ps->fill); + #else + #warning "GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on" + gdispGFillArea(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+(ARROW/2+1), ARROW, ARROW, ps->fill); + gdispGFillArea(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+gw->g.height-(ARROW+ARROW/2+1), ARROW, ARROW, ps->fill); + #endif + } else + iwidth = gw->g.width - 2; + + #if GWIN_NEED_LIST_IMAGES + if ((gw->g.flags & GLIST_FLG_HASIMAGES)) { + x += iheight; + iwidth -= iheight; + } + #endif + + + // Find the top item + for (qi = gfxQueueASyncPeek(&gw2obj->list_head), i = iheight - 1; i < gw2obj->top && qi; qi = gfxQueueASyncNext(qi), i+=iheight); + + // the list frame + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, ps->edge); + + // Set the clipping region so we do not override the frame. + gdispGSetClip(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2); + + // Draw until we run out of room or items + for (y = 1-(gw2obj->top%iheight); y < gw->g.height-2 && qi; qi = gfxQueueASyncNext(qi), y += iheight) { + fill = (qi2li->flags & GLIST_FLG_SELECTED) ? ps->fill : gw->pstyle->background; + gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, iheight, fill); + #if GWIN_NEED_LIST_IMAGES + if ((gw->g.flags & GLIST_FLG_HASIMAGES)) { + // Clear the image area + if (qi2li->pimg && gdispImageIsOpen(qi2li->pimg)) { + // Calculate which image + sy = (qi2li->flags & GLIST_FLG_SELECTED) ? 0 : (iheight-VERTICAL_PADDING); + if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) + sy += 2*(iheight-VERTICAL_PADDING); + while (sy > qi2li->pimg->height) + sy -= iheight-VERTICAL_PADDING; + // Draw the image + gdispImageSetBgColor(qi2li->pimg, fill); + gdispGImageDraw(gw->g.display, qi2li->pimg, gw->g.x+1, gw->g.y+y, iheight-VERTICAL_PADDING, iheight-VERTICAL_PADDING, 0, sy); + } + } + #endif + gdispGFillStringBox(gw->g.display, gw->g.x+x+HORIZONTAL_PADDING, gw->g.y+y, iwidth-HORIZONTAL_PADDING, iheight, qi2li->text, gw->g.font, ps->text, fill, justifyLeft); + } + + // Fill any remaining item space + if (y < gw->g.height-1) + gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, gw->g.height-1-y, gw->pstyle->background); +} + +#endif // GFX_USE_GWIN && GWIN_NEED_LIST diff --git a/src/gwin/gwin_list.h b/src/gwin/gwin_list.h new file mode 100644 index 00000000..a55d4cb8 --- /dev/null +++ b/src/gwin/gwin_list.h @@ -0,0 +1,304 @@ +/* + * 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_list.h + * @brief GWIN list widget header file + * + * @defgroup List List + * @ingroup Widgets + * + * @details GWIN allows it to create a list widget. + * + * @pre GFX_USE_GDISP must be set to TRUE in your gfxconf.h + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GDISP_NEED_TEXT must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_LIST must be set to TRUE in your gfxconf.h + * @pre The font you want to use must be enabled in your gfxconf.h + * + * @{ + */ + +#ifndef _GWIN_LIST_H +#define _GWIN_LIST_H + +// This file is included within "src/gwin/gwin_widget.h" + +/** + * @brief The event type for a list event + */ +#define GEVENT_GWIN_LIST (GEVENT_GWIN_CTRL_FIRST+4) + +/** + * @brief A list event + */ +typedef struct GEventGWinList { + GEventType type; // The type of this event (GEVENT_GWIN_LIST) + GHandle gwin; // The list + #if GWIN_WIDGET_TAGS + WidgetTag tag; // The list tag + #endif + int item; // The item that has been selected (or unselected in a multi-select listbox) +} GEventGWinList; + +// A list window +typedef struct GListObject { + GWidgetObject w; + + #if GINPUT_NEED_MOUSE + coord_t start_mouse_x; + coord_t start_mouse_y; + coord_t last_mouse_y; + #endif + #if GINPUT_NEED_TOGGLE + uint16_t t_up; + uint16_t t_dn; + #endif + + int cnt; // Number of items currently in the list (quicker than counting each time) + int top; // Viewing offset in pixels from the top of the list + gfxQueueASync list_head; // The list of items +} GListObject; + +/** + * @brief Enum to change the behaviour of the scroll bar + * + * @note Used with @p gwinListSetScroll() + * @note @p scrollAlways always show the scrollbar + * @note @p scrollAuto show the scrollbar when there are more items on the list then fit on the screen + * @note @p scrollSmooth enable touch screen smooth scrolling + */ +typedef enum scroll_t { scrollAlways, scrollAuto, scrollSmooth } scroll_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a list widget + * + * @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 Black and White. + * @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 not display anything. + * @note A list remembers its normal drawing state. If there is a window manager then it is automatically + * redrawn if the window is moved or its visibility state is changed. + * @note The list contains no elements after creation. + * @note A slider supports mouse, toggle. Note: toggle only works correctly for single-select lists. + * @note When assigning a toggle, only one toggle is supported per role. If you try to assign more than + * one toggle to a role, it will forget the previous toggle. Two roles are supported: + * Role 0 = toggle for down, role 1 = toggle for up + * + * @param[in] g The GDisplay to display this window on + * @param[in] widget The GListObject structure to initialize. If this is NULL, the structure is dynamically allocated. + * @param[in] pInit The initialization parameters to use + * @param[in] multiselect If TRUE the list is multi-select instead of single-select. + * + * @return NULL if there is no resulting drawing area, otherwise a window handle. + * + * @api + */ +GHandle gwinGListCreate(GDisplay *g, GListObject *widget, GWidgetInit *pInit, bool_t multiselect); +#define gwinListCreate(w, pInit, m) gwinGListCreate(GDISP, w, pInit, m) + +/** + * @brief Enable or disable the rendering of the list + * + * @details Usually the list is being re-rendered when an item is added to the list. This can cause + * flickering and performance issues when many items are added at once. This can be prevented + * by temporarely disabling the render using this function. + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] ena TRUE or FALSE + * + * @api + */ +void gwinListEnableRender(GHandle gh, bool_t ena); + +/** + * @brief Change the behaviour of the scroll bar + * + * @note Current possible values: @p scrollAlways, @p scrollAuto and @p scrollSmooth + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] flag The behaviour to be set + * + * @api + */ +void gwinListSetScroll(GHandle gh, scroll_t flag); + +/** + * @brief Add an item to the list + * + * @note The ID you get returned is not static. If items get removed from the list, the list items get + * reordered. + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] item The string which shall be displayed in the list afterwards + * @param[in] useAlloc If set to TRUE, the string will be dynamically allocated. A static buffer must be passed otherwise + * + * @return The current ID of the item. The ID might change if you remove items from the middle of the list + * + * @api + */ +int gwinListAddItem(GHandle gh, const char* item, bool_t useAlloc); + +/** + * @brief Get the name behind an item with a given ID + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] item The item ID + * + * @return The string of the list item or NULL on error + * + * @api + */ +const char* gwinListItemGetText(GHandle gh, int item); + +/** + * @brief Get the ID of an item with a given name + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] text The item name + * + * @return The id of the list item or -1 on error + * + * @api + */ +int gwinListFindText(GHandle gh, const char* text); + +/** + * @brief Set the custom parameter of an item with a given ID + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] item The item ID + * @param[in] param The parameter to be set + * + * @api + */ +void gwinListItemSetParam(GHandle gh, int item, uint16_t param); + +/** + * @brief Get the custom parameter of an item with a given ID + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] item The item ID + * + * @return The parameter + * + * @api + */ +uint16_t gwinListItemGetParam(GHandle gh, int item); + +/** + * @brief Delete all the items of the list + * + * @param[in] gh The widget handle (must be a list handle) + * + * @api + */ +void gwinListDeleteAll(GHandle gh); + +/** + * @brief Delete an item from the list + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] item The item ID + * + * @api + */ +void gwinListItemDelete(GHandle gh, int item); + +/** + * @brief Get the amount of items within the list + * + * @param[in] gh The widget handle (must be a list handle) + * + * @return The amount of items in the list + * + * @api + */ +int gwinListItemCount(GHandle gh); + +/** + * @brief Check if an item with a given ID is selected + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] item The item ID + * + * @return TRUE if the item is selected, FALSE otherwise + * + * @api + */ +bool_t gwinListItemIsSelected(GHandle gh, int item); + +/** + * @brief Get the ID of the selected item + * + * @param[in] gh The widget handle (must be a list handle) + * + * @return The ID of the selected list item for a single-select list. + * + * @note It always returns -1 (nothing selected) for a multi-select list. + * Instead use @p gwinListItemIsSelected() to get the selection status + * for a multi-select list. + * + * @api + */ +int gwinListGetSelected(GHandle gh); + +/** + * @brief Get the text of the selected item + * + * @param[in] gh The widget handle (must be a list handle) + * + * @return The test of the selected list item for a single-select list. + * + * @note It always returns NULL (nothing selected) for a multi-select list. + * + * @api + */ +const char* gwinListGetSelectedText(GHandle gh); + +#if GWIN_NEED_LIST_IMAGES || defined(__DOXYGEN__) + /** + * @brief Set the image for a list item + * + * @pre GWIN_NEED_LIST_IMAGES must be set to true in your gfxconf.h + * + * @param[in] gh The widget handle (must be a list handle) + * @param[in] item The item ID + * @param[in] pimg The image to be displayed or NULL to turn off the image for this list item. + * + * @note The image should be up to 4 x (the font height) and a width of (the font height). + * The 1st (top) part of the image is displayed for a selected item. + * The 2nd part of the image is displayed for an unselected item. + * The 3rd part of the image is displayed for a disabled selected item. + * The 4th part of the image is displayed for a disabled unselected item. + * If the image is less than 4 times the font height then the image use is collapsed + * into the available height. For example, an image that equals the font height will use + * the same image for all four states. + * @note The image is only displayed while it is open. It is up to the application to open + * the image. + * @note The same image can be used on more than one list item. + * @note Images are aligned with the top (not the baseline) of the list item. + * @note When any item in the list has an image attached, space is allocated to display + * the images even if the image is closed or has been removed by calling @p gwinListItemSetImage() + * with a NULL image or by calling @p gwinListItemDelete(). The only way to turn-off the image area + * for this list is to call gwinListDeleteAll(). + * + */ + void gwinListItemSetImage(GHandle gh, int item, gdispImage *pimg); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // _GWIN_LIST_H +/** @} */ + diff --git a/src/gwin/gwin_progressbar.c b/src/gwin/gwin_progressbar.c new file mode 100644 index 00000000..ef28e8dc --- /dev/null +++ b/src/gwin/gwin_progressbar.c @@ -0,0 +1,316 @@ +/* + * 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_progressbar.c + * @brief GWIN sub-system progressbar code + */ + +#include "gfx.h" + +#if (GFX_USE_GWIN && GWIN_NEED_PROGRESSBAR) || defined(__DOXYGEN__) + +#include "gwin_class.h" + +// Reset the display position back to the value predicted by the saved progressbar position +static void ResetDisplayPos(GProgressbarObject *gsw) { + if (gsw->w.g.width < gsw->w.g.height) + gsw->dpos = gsw->w.g.height-1-((gsw->w.g.height-1)*(gsw->pos-gsw->min))/(gsw->max-gsw->min); + else + gsw->dpos = ((gsw->w.g.width-1)*(gsw->pos-gsw->min))/(gsw->max-gsw->min); +} + +// We have to deinitialize the timer which auto updates the progressbar if any +static void _destroy(GHandle gh) { + #if GWIN_PROGRESSBAR_AUTO + gtimerDeinit( &((GProgressbarObject *)gh)->gt ); + #endif + + _gwidgetDestroy(gh); +} + +// The progressbar VMT table +static const gwidgetVMT progressbarVMT = { + { + "Progressbar", // The classname + sizeof(GProgressbarObject), // The object size + _destroy, // The destroy routine + _gwidgetRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinProgressbarDraw_Std, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + 0, // Process mouse down events (NOT USED) + 0, // Process mouse up events + 0, // Process mouse move events + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 0, // 1 toggle role + 0, // Assign Toggles + 0, // Get Toggles + 0, // Process toggle off events (NOT USED) + 0, // Process toggle on events + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, // 1 dial roles + 0, // Assign Dials + 0, // Get Dials + 0, // Process dial move events + }, + #endif +}; + +GHandle gwinGProgressbarCreate(GDisplay *g, GProgressbarObject *gs, const GWidgetInit *pInit) { + if (!(gs = (GProgressbarObject *)_gwidgetCreate(g, &gs->w, pInit, &progressbarVMT))) + return 0; + + gs->min = 0; + gs->max = 100; + gs->res = 1; + gs->pos = 0; + + #if GWIN_PROGRESSBAR_AUTO + gs->delay = 0; + gtimerInit(&gs->gt); + #endif + + ResetDisplayPos(gs); + gwinSetVisible((GHandle)gs, pInit->g.show); + + return (GHandle)gs; +} + +void gwinProgressbarSetRange(GHandle gh, int min, int max) { + #define gsw ((GProgressbarObject *)gh) + + if (gh->vmt != (gwinVMT *)&progressbarVMT) + return; + + if (min == max) // prevent divide by 0 errors. + max++; + gsw->min = min; + gsw->max = max; + gsw->pos = min; + + ResetDisplayPos(gsw); + + #undef gsw +} + +void gwinProgressbarSetPosition(GHandle gh, int pos) { + #define gsw ((GProgressbarObject *)gh) + + if (gh->vmt != (gwinVMT *)&progressbarVMT) + return; + + if (gsw->min <= gsw->max) { + if (pos < gsw->min) gsw->pos = gsw->min; + else if (pos > gsw->max) gsw->pos = gsw->max; + else gsw->pos = pos; + } else { + if (pos > gsw->min) gsw->pos = gsw->min; + else if (pos < gsw->max) gsw->pos = gsw->max; + else gsw->pos = pos; + } + + ResetDisplayPos(gsw); + _gwinUpdate(gh); + + #undef gsw +} + +void gwinProgressbarSetResolution(GHandle gh, int resolution) { + #define gsw ((GProgressbarObject *)gh) + + if (gh->vmt != (gwinVMT *)&progressbarVMT) + return; + + if (resolution <= 0) + resolution = 1; + + gsw->res = resolution; + + #undef gsw +} + +void gwinProgressbarIncrement(GHandle gh) { + #define gsw ((GProgressbarObject *)gh) + + if (gh->vmt != (gwinVMT *)&progressbarVMT) + return; + + if (gsw->max - gsw->pos > gsw->res) + gsw->pos += gsw->res; + else + gsw->pos = gsw->max; + + ResetDisplayPos(gsw); + _gwinUpdate(gh); + + #undef gsw +} + +void gwinProgressbarDecrement(GHandle gh) { + #define gsw ((GProgressbarObject *)gh) + + if (gh->vmt != (gwinVMT *)&progressbarVMT) + return; + + if (gsw->pos > gsw->res) + gsw->pos -= gsw->min; + else + gsw->pos = gsw->min; + + gsw->pos -= gsw->res; + + ResetDisplayPos(gsw); + _gwinUpdate(gh); + + #undef gsw +} + +#if GWIN_PROGRESSBAR_AUTO + // used by gwinProgressbarStart(); + static void _progressbarCallback(void *param) { + #define gsw ((GProgressbarObject *)gh) + GHandle gh = (GHandle)param; + + if (gh->vmt != (gwinVMT *)&progressbarVMT) + return; + + gwinProgressbarIncrement(gh); + + if (gsw->pos < gsw->max) + gtimerStart(&(gsw->gt), _progressbarCallback, gh, FALSE, gsw->delay); + + #undef gsw + } + + void gwinProgressbarStart(GHandle gh, delaytime_t delay) { + #define gsw ((GProgressbarObject *)gh) + + if (gh->vmt != (gwinVMT *)&progressbarVMT) + return; + + gsw->delay = delay; + + gtimerStart(&(gsw->gt), _progressbarCallback, gh, FALSE, gsw->delay); + + #undef gsw + } + + void gwinProgressbarStop(GHandle gh) { + #define gsw ((GProgressbarObject *)gh) + + if (gh->vmt != (gwinVMT *)&progressbarVMT) + return; + + gtimerStop(&(gsw->gt)); + + #undef gsw + } +#endif /* GWIN_PROGRESSBAR_AUTO */ + +/*---------------------------------------------------------- + * Custom Draw Routines + *----------------------------------------------------------*/ + +void gwinProgressbarDraw_Std(GWidgetObject *gw, void *param) { + #define gsw ((GProgressbarObject *)gw) + + const GColorSet * pcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&progressbarVMT) + return; + + // get the colors right + if ((gw->g.flags & GWIN_FLG_SYSENABLED)) + pcol = &gw->pstyle->pressed; + else + pcol = &gw->pstyle->disabled; + + // Vertical progressbar + if (gw->g.width < gw->g.height) { + if (gsw->dpos != gw->g.height-1) + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.width, gw->g.height - gsw->dpos, pcol->progress); // Active Area + if (gsw->dpos != 0) + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, gw->pstyle->enabled.progress); // Inactive area + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb + + // Horizontal progressbar + } else { + if (gsw->dpos != gw->g.width-1) + gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.width-gsw->dpos, gw->g.height, gw->pstyle->enabled.progress); // Inactive area + if (gsw->dpos != 0) + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gsw->dpos, gw->g.height, pcol->progress); // Active Area + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge + gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb + } + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + + #undef gsw +} + +#if GDISP_NEED_IMAGE +void gwinProgressbarDraw_Image(GWidgetObject *gw, void *param) { + #define gsw ((GProgressbarObject *)gw) + #define gi ((gdispImage *)param) + const GColorSet * pcol; + coord_t z, v; + + if (gw->g.vmt != (gwinVMT *)&progressbarVMT) + return; + + if ((gw->g.flags & GWIN_FLG_SYSENABLED)) + pcol = &gw->pstyle->pressed; + else + pcol = &gw->pstyle->disabled; + + if (gw->g.width < gw->g.height) { // Vertical progressbar + if (gsw->dpos != 0) // The unfilled area + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, gw->pstyle->enabled.progress); // Inactive area + if (gsw->dpos != gw->g.height-1) { // The filled area + for(z=gw->g.height, v=gi->height; z > gsw->dpos;) { + z -= v; + if (z < gsw->dpos) { + v -= gsw->dpos - z; + z = gsw->dpos; + } + gdispGImageDraw(gw->g.display, gi, gw->g.x, gw->g.y+z, gw->g.width, v, 0, gi->height-v); + } + } + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb + + // Horizontal progressbar + } else { + if (gsw->dpos != gw->g.width-1) // The unfilled area + gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.width-gsw->dpos, gw->g.height, gw->pstyle->enabled.progress); // Inactive area + if (gsw->dpos != 0) { // The filled area + for(z=0, v=gi->width; z < gsw->dpos; z += v) { + if (z+v > gsw->dpos) + v -= z+v - gsw->dpos; + gdispGImageDraw(gw->g.display, gi, gw->g.x+z, gw->g.y, v, gw->g.height, 0, 0); + } + } + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge + gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb + } + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + + #undef gsw +} +#endif /* GDISP_NEED_IMAGE */ + +#endif /* GFX_USE_GWIN && GWIN_NEED_BUTTON */ diff --git a/src/gwin/gwin_progressbar.h b/src/gwin/gwin_progressbar.h new file mode 100644 index 00000000..49c11a98 --- /dev/null +++ b/src/gwin/gwin_progressbar.h @@ -0,0 +1,210 @@ +/* + * 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_progressbar.h + * @brief GWIN Graphic window subsystem header file. + * + * @defgroup Progressbar Progressbar + * @ingroup Widgets + * + * @details Create progressbars with different styles + * + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_PROGRESSBAR must be set to TRUE in your gfxconf.h + * @{ + */ + +#ifndef _GWIN_PROGRESSBAR_H +#define _GWIN_PROGRESSBAR_H + +/* This file is included within src/gwin/gwin_widget.h */ +// A progressbar window +typedef struct GProgressbarObject { + GWidgetObject w; + coord_t dpos; + int min; + int max; + int res; + int pos; + #if GWIN_PROGRESSBAR_AUTO + GTimer gt; + delaytime_t delay; + #endif +} GProgressbarObject; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a progressbar 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] gb The GProgressbarObject structure to initialise. If this is NULL the structure is dynamically allocated. + * @param[in] pInit The initialization parameters to use + * + * @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 progressbar remembers its normal drawing state. If there is a window manager then it is automatically + * redrawn if the window is moved or its visibility state is changed. + * @note The initial progressbar range is from 0 to 100 with an initial position of 0. + * @note A progressbar does not take any GINPUT inputs. + * + * @api + */ +GHandle gwinGProgressbarCreate(GDisplay *g, GProgressbarObject *gb, const GWidgetInit *pInit); +#define gwinProgressbarCreate(w, pInit) gwinGProgressbarCreate(GDISP, w, pInit) + +/** + * @brief Set the progressbar range. + * + * @param[in] gh The window handle (must be a progressbar window) + * @param[in] min The minimum value + * @param[in] max The maximum value + * + * @note The defaults are 0 and 100 + * @note Sets the position to the minimum value. + * @note The progressbar is not automatically drawn. Call gwinProgressbarDraw() after changing the range. + * + * @api + */ +void gwinProgressbarSetRange(GHandle gh, int min, int max); + +/** + * @brief Set the progressbar position. + * + * @param[in] gh The window handle (must be a progressbar window) + * @param[in] pos The new position + * + * @note If the new position is outside the progressbar range then the position + * is set to the closest end of the range. + * @note The progressbar is not automatically drawn. Call gwinProgressbarDraw() after changing the position. + * + * @api + */ +void gwinProgressbarSetPosition(GHandle gh, int pos); + +/** + * @brief Set the resolution for the incrementation and decrementation of the progressbar + * + * @note Default is set to 1 + * + * @param[in] gh The window handle (must be a progressbar window) + * @param[in] res The resolution to be set + * + * @api + */ +void gwinProgressbarSetResolution(GHandle gh, int res); + +/** + * @brief Increment the progressbar value + * + * @details Increments by the resolution set through gwinProgressbarSetResolution() + * + * @param[in] gh The window handle (must be a progressbar window) + * + * @api + */ +void gwinProgressbarIncrement(GHandle gh); + +/** + * @brief Decrement the progressbar value + * + * @details Decrements by the resolution set through gwinProgressbarSetResolution() + * + * @param[in] gh The window handle (must be a progressbar window) + * + * @api + */ +void gwinProgressbarDecrement(GHandle gh); + +/** + * @brief Get the current progressbar position. + * @return The progressbar position + * + * @param[in] gh The window handle (must be a progressbar window) + * + * @note The use of a listener to get the progressbar position is recommended if you + * want continuous updates on the progressbar position. + * + * @api + */ +#define gwinProgressbarGetPosition(gh) (((GProgressbarObject *)(gh))->pos) + + /** + * @brief Reset the progressbar to the minimum position + * + * @param[in] gh The window handle (must be a progressbar window) + * + * @api + */ +#define gwinProgressbarReset(gh) gwinProgressbarSetPosition(gh, ((GProgressbarObject *)(gh))->min) + +#if GWIN_PROGRESSBAR_AUTO + /** + * @brief Automatically increments the progress bar + * + * @note The delay is generated using the GTIMER module which is based on software/virtual timer. + * Therefore, the delay is totally unprecise. + * + * @note The progressbar incrementation starts at the current level. It is not reset to the minimum value. + * + * @note An event is generated once the maximum value has been reached (ToDo) + * + * @param[in] gh The window handle (must be a progressbar window) + * @param[in] delay The incrementation delay (in milliseconds) + * + * @api + */ + void gwinProgressbarStart(GHandle gh, delaytime_t delay); + + /** + * @brief Stop the timer which is started by @p gwinProgressbarStart() + * + * @param[in] gh The window handle (must be a progressbar window) + * + * @api + */ + void gwinProgressbarStop(GHandle gh); +#endif /* GWIN_PROGRESSBAR_AUTO */ + +/** + * @brief Some custom progressbar drawing routines + * @details These function may be passed to @p gwinSetCustomDraw() to get different progressbar drawing styles + * + * @param[in] gw The widget (which must be a progressbar) + * @param[in] param A parameter passed in from the user + * + * @note In your custom progressbar drawing function you may optionally call this + * standard functions and then draw your extra details on top. + * @note The standard functions below ignore the param parameter except for @p gwinProgressbarDraw_Image(). + * @note The image custom draw function @p gwinProgressbarDraw_Image() uses param to pass in the gdispImage pointer. + * The image must be already opened before calling @p gwinSetCustomDraw(). The image is tiled to fill + * the active area of the progressbar. The normal colors apply to the border and inactive area and the dividing line + * between the active and inactive areas. + * No checking is done to compare the dimensions of the progressbar to the size of the image. + * Note text is drawn on top of the image. + * @note These custom drawing routines don't have to worry about setting clipping as the framework + * sets clipping to the object window prior to calling these routines. + * + * @api + * @{ + */ +void gwinProgressbarDraw_Std(GWidgetObject *gw, void *param); +void gwinProgressbarDraw_Image(GWidgetObject *gw, void *param); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GWIN_PROGRESSBAR_H */ +/** @} */ diff --git a/src/gwin/gwin_radio.c b/src/gwin/gwin_radio.c new file mode 100644 index 00000000..0bafa7cc --- /dev/null +++ b/src/gwin/gwin_radio.c @@ -0,0 +1,280 @@ +/* + * 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_radio.c + * @brief GWIN sub-system radio button code + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_RADIO + +#include "gwin_class.h" + +#define GRADIO_TAB_CNR 8 // Diagonal corner on active tab +#define GRADIO_TOP_FADE 50 // (GRADIO_TOP_FADE/255)% fade to white for top of tab/button +#define GRADIO_BOTTOM_FADE 25 // (GRADIO_BOTTOM_FADE/255)% fade to black for bottom of tab/button +#define GRADIO_OUTLINE_FADE 128 // (GRADIO_OUTLINE_FADE/255)% fade to background for active tab edge + +// Our pressed state +#define GRADIO_FLG_PRESSED (GWIN_FIRST_CONTROL_FLAG<<0) + +// Send the button event +static void SendRadioEvent(GWidgetObject *gw) { + GSourceListener * psl; + GEvent * pe; + #define pbe ((GEventGWinRadio *)pe) + + // Trigger a GWIN Button Event + psl = 0; + while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { + if (!(pe = geventGetEventBuffer(psl))) + continue; + pbe->type = GEVENT_GWIN_RADIO; + pbe->gwin = (GHandle)gw; + pbe->group = ((GRadioObject *)gw)->group; + #if GWIN_WIDGET_TAGS + pbe->tag = gw->tag; + #endif + geventSendEvent(psl); + } + + #undef pbe +} + +#if GINPUT_NEED_MOUSE + // A mouse down has occurred over the button + static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { + (void) x; (void) y; + + gwinRadioPress((GHandle)gw); + } +#endif + +#if GINPUT_NEED_TOGGLE + // A toggle on has occurred + static void ToggleOn(GWidgetObject *gw, uint16_t role) { + (void) role; + + gwinRadioPress((GHandle)gw); + } + + static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { + (void) role; + ((GRadioObject *)gw)->toggle = instance; + } + + static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { + (void) role; + return ((GRadioObject *)gw)->toggle; + } +#endif + +// The radio button VMT table +static const gwidgetVMT radioVMT = { + { + "Radio", // The classname + sizeof(GRadioObject), // The object size + _gwidgetDestroy, // The destroy routine + _gwidgetRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinRadioDraw_Radio, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + MouseDown, // Process mouse down events + 0, // Process mouse up events (NOT USED) + 0, // Process mouse move events (NOT USED) + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 1, // 1 toggle role + ToggleAssign, // Assign Toggles + ToggleGet, // Get Toggles + 0, // Process toggle off events (NOT USED) + ToggleOn, // Process toggle on events + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, // No dial roles + 0, // Assign Dials (NOT USED) + 0, // Get Dials (NOT USED) + 0, // Process dial move events (NOT USED) + }, + #endif +}; + +GHandle gwinGRadioCreate(GDisplay *g, GRadioObject *gw, const GWidgetInit *pInit, uint16_t group) { + if (!(gw = (GRadioObject *)_gwidgetCreate(g, &gw->w, pInit, &radioVMT))) + return 0; + + #if GINPUT_NEED_TOGGLE + gw->toggle = GWIDGET_NO_INSTANCE; + #endif + gw->group = group; + gwinSetVisible((GHandle)gw, pInit->g.show); + return (GHandle)gw; +} + +void gwinRadioPress(GHandle gh) { + GHandle gx; + + if (gh->vmt != (gwinVMT *)&radioVMT || (gh->flags & GRADIO_FLG_PRESSED)) + return; + + if ((gx = gwinRadioGetActive(((GRadioObject *)gh)->group))) { + gx->flags &= ~GRADIO_FLG_PRESSED; + _gwinUpdate(gx); + } + gh->flags |= GRADIO_FLG_PRESSED; + _gwinUpdate(gh); + SendRadioEvent((GWidgetObject *)gh); +} + +bool_t gwinRadioIsPressed(GHandle gh) { + if (gh->vmt != (gwinVMT *)&radioVMT) + return FALSE; + + return (gh->flags & GRADIO_FLG_PRESSED) ? TRUE : FALSE; +} + +GHandle gwinRadioGetActive(uint16_t group) { + GHandle gh; + + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if (gh->vmt == (gwinVMT *)&radioVMT && ((GRadioObject *)gh)->group == group && (gh->flags & GRADIO_FLG_PRESSED)) + return gh; + } + return 0; +} + +/*---------------------------------------------------------- + * Custom Draw Routines + *----------------------------------------------------------*/ + +static const GColorSet *getDrawColors(GWidgetObject *gw) { + if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled; + if ((gw->g.flags & GRADIO_FLG_PRESSED)) return &gw->pstyle->pressed; + return &gw->pstyle->enabled; +} + +void gwinRadioDraw_Radio(GWidgetObject *gw, void *param) { + #define gcw ((GRadioObject *)gw) + coord_t ld, df; + const GColorSet * pcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&radioVMT) return; + pcol = getDrawColors(gw); + + ld = gw->g.width < gw->g.height ? gw->g.width : gw->g.height; + + #if GDISP_NEED_CIRCLE + df = (ld-1)/2; + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, ld, ld, gw->pstyle->background); + gdispGDrawCircle(gw->g.display, gw->g.x+df, gw->g.y+df, df, pcol->edge); + + if (gw->g.flags & GRADIO_FLG_PRESSED) + gdispGFillCircle(gw->g.display, gw->g.x+df, gw->g.y+df, df <= 2 ? 1 : (df-2), pcol->fill); + #else + gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+1, ld, ld-2, gw->pstyle->background); + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, ld, ld, pcol->edge); + + df = ld < 4 ? 1 : 2; + if (gw->g.flags & GRADIO_FLG_PRESSED) + gdispGFillArea(gw->g.display, gw->g.x+df, gw->g.y+df, ld-2*df, ld-2*df, pcol->fill); + #endif + + gdispGFillStringBox(gw->g.display, gw->g.x+ld+1, gw->g.y, gw->g.width-ld-1, gw->g.height, gw->text, gw->g.font, pcol->text, gw->pstyle->background, justifyLeft); + #undef gcw +} + +#if GWIN_FLAT_STYLING + void gwinRadioDraw_Button(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&radioVMT) return; + pcol = getDrawColors(gw); + + gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); + gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); + } + void gwinRadioDraw_Tab(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&radioVMT) return; + pcol = getDrawColors(gw); + + if ((gw->g.flags & GRADIO_FLG_PRESSED)) { + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); + gdispGFillStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); + } else { + gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); + gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); + } + } +#else + void gwinRadioDraw_Button(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + fixed alpha; + fixed dalpha; + coord_t i; + color_t tcol, bcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&radioVMT) return; + pcol = getDrawColors(gw); + + /* Fill the box blended from variants of the fill color */ + tcol = gdispBlendColor(White, pcol->fill, GRADIO_TOP_FADE); + bcol = gdispBlendColor(Black, pcol->fill, GRADIO_BOTTOM_FADE); + dalpha = FIXED(255)/gw->g.height; + for(alpha = 0, i = 0; i < gw->g.height; i++, alpha += dalpha) + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+i, gw->g.x+gw->g.width-2, gw->g.y+i, gdispBlendColor(bcol, tcol, NONFIXED(alpha))); + + gdispGDrawStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, justifyCenter); + gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); + } + void gwinRadioDraw_Tab(GWidgetObject *gw, void *param) { + const GColorSet * pcol; + fixed alpha; + fixed dalpha; + coord_t i; + color_t tcol, bcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&radioVMT) return; + pcol = getDrawColors(gw); + + if ((gw->g.flags & GRADIO_FLG_PRESSED)) { + tcol = gdispBlendColor(pcol->edge, gw->pstyle->background, GRADIO_OUTLINE_FADE); + gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->text, gw->g.font, pcol->text, gw->g.bgcolor, justifyCenter); + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y, gw->g.x+gw->g.width-(GRADIO_TAB_CNR+1), gw->g.y, tcol); + gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-(GRADIO_TAB_CNR+1), gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+GRADIO_TAB_CNR, tcol); + gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y+GRADIO_TAB_CNR, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, tcol); + } else { + /* Fill the box blended from variants of the fill color */ + tcol = gdispBlendColor(White, pcol->fill, GRADIO_TOP_FADE); + bcol = gdispBlendColor(Black, pcol->fill, GRADIO_BOTTOM_FADE); + dalpha = FIXED(255)/gw->g.height; + for(alpha = 0, i = 0; i < gw->g.height; i++, alpha += dalpha) + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+i, gw->g.x+gw->g.width-2, gw->g.y+i, gdispBlendColor(bcol, tcol, NONFIXED(alpha))); + gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + } + } +#endif + +#endif /* GFX_USE_GWIN && GWIN_NEED_BUTTON */ diff --git a/src/gwin/gwin_radio.h b/src/gwin/gwin_radio.h new file mode 100644 index 00000000..d5c9c21a --- /dev/null +++ b/src/gwin/gwin_radio.h @@ -0,0 +1,144 @@ +/* + * 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_radio.h + * @brief GWIN Graphic window subsystem header file. + * + * @defgroup RadioButton RadioButton + * @ingroup Widgets + * + * @details GWIN allows it to easily create radio buttons with different styles. + * + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_RADIO must be set to TRUE in your gfxconf.h + * @{ + */ + +#ifndef _GWIN_RADIO_H +#define _GWIN_RADIO_H + +/* This file is included within "src/gwin/gwin_widget.h" */ + +/** + * @brief The Event Type for a Radio Event + */ +#define GEVENT_GWIN_RADIO (GEVENT_GWIN_CTRL_FIRST+3) + +/** + * @brief A Button Event + * @note There are currently no GEventGWinRadio listening flags - use 0 as the flags to @p gwinAttachListener() + */ +typedef struct GEventGWinRadio { + GEventType type; // The type of this event (GEVENT_GWIN_RADIO) + GHandle gwin; // The radio button that has been depressed + #if GWIN_WIDGET_TAGS + WidgetTag tag; // The radio tag + #endif + uint16_t group; // The group for this radio button +} GEventGWinRadio; + +/** + * @brief The radio button widget structure + * @note Do not use the members directly - treat it as a black-box. + */ +typedef struct GRadioObject { + GWidgetObject w; + #if GINPUT_NEED_TOGGLE + uint16_t toggle; + #endif + uint16_t group; +} GRadioObject; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a radio widget. + * @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] gb The GRadioObject structure to initialise. If this is NULL the structure is dynamically allocated. + * @param[in] pInit The initialisation parameters + * @param[in] group The group of radio buttons this radio button belongs to. + * + * @note Only one radio button in any group is ever pressed at one time. Pressing one radio button will + * release all others in the group. + * @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 radio button remembers its normal drawing state. If there is a window manager then it is automatically + * redrawn if the window is moved or its visibility state is changed. + * @note A radio button supports mouse and a toggle input. + * @note When assigning a toggle, only one toggle is supported. If you try to assign more than one toggle it will + * forget the previous toggle. When assigning a toggle the role parameter must be 0. + * + * @api + */ +GHandle gwinGRadioCreate(GDisplay *g, GRadioObject *gb, const GWidgetInit *pInit, uint16_t group); +#define gwinRadioCreate(w, pInit, gr) gwinGRadioCreate(GDISP, w, pInit, gr) + +/** + * @brief Press this radio button (and by definition unset any others in the group) + * + * @param[in] gh The window handle (must be a radio widget) + * + * @api + */ +void gwinRadioPress(GHandle gh); + +/** + * @brief Is the radio button currently pressed + * @return TRUE if the button is pressed + * + * @param[in] gh The window handle (must be a radio widget) + * + * @api + */ +bool_t gwinRadioIsPressed(GHandle gh); + +/** + * @brief Find the currently pressed radio button in the specified group + * @return The handle of the pressed radio button or NULL if none are pressed + * + * @param[in] group The radio button group to be examined + * + * @return The handle of the currently pressed radio button + * + * @api + */ +GHandle gwinRadioGetActive(uint16_t group); + +/** + * @brief Some custom radio button drawing routines + * @details These function may be passed to @p gwinSetCustomDraw() to get different radio button drawing styles + * + * @param[in] gw The widget object (in this case a radio button) + * @param[in] param A parameter passed in from the user + * + * @note In your custom radio drawing function you may optionally call these + * standard functions and then draw your extra details on top. + * @note The standard functions below ignore the param parameter. + * @note These custom drawing routines don't have to worry about setting clipping as the framework + * sets clipping to the object window prior to calling these routines. + * + * @api + * @{ + */ +void gwinRadioDraw_Radio(GWidgetObject *gw, void *param); // @< A standard radio button +void gwinRadioDraw_Button(GWidgetObject *gw, void *param); // @< Draw as a button +void gwinRadioDraw_Tab(GWidgetObject *gw, void *param); // @< Draw as a tab +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GWIN_RADIO_H */ +/** @} */ diff --git a/src/gwin/gwin_slider.c b/src/gwin/gwin_slider.c new file mode 100644 index 00000000..f1230d5a --- /dev/null +++ b/src/gwin/gwin_slider.c @@ -0,0 +1,376 @@ +/* + * 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_slider.c + * @brief GWIN sub-system slider code + */ + +#include "gfx.h" + +#if (GFX_USE_GWIN && GWIN_NEED_SLIDER) || defined(__DOXYGEN__) + +#include "gwin_class.h" + +#ifndef GWIN_SLIDER_DEAD_BAND + #define GWIN_SLIDER_DEAD_BAND 5 +#endif + +#ifndef GWIN_SLIDER_TOGGLE_INC + #define GWIN_SLIDER_TOGGLE_INC 20 // How many toggles to go from minimum to maximum +#endif + +// Send the slider event +static void SendSliderEvent(GWidgetObject *gw) { + GSourceListener * psl; + GEvent * pe; + #define pse ((GEventGWinSlider *)pe) + + // Trigger a GWIN Button Event + psl = 0; + while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { + if (!(pe = geventGetEventBuffer(psl))) + continue; + pse->type = GEVENT_GWIN_SLIDER; + pse->gwin = (GHandle)gw; + pse->position = ((GSliderObject *)gw)->pos; + #if GWIN_WIDGET_TAGS + pse->tag = gw->tag; + #endif + geventSendEvent(psl); + } + + #undef pse +} + +// Reset the display position back to the value predicted by the saved slider position +static void ResetDisplayPos(GSliderObject *gsw) { + if (gsw->w.g.width < gsw->w.g.height) + gsw->dpos = gsw->w.g.height-1-((gsw->w.g.height-1)*(gsw->pos-gsw->min))/(gsw->max-gsw->min); + else + gsw->dpos = ((gsw->w.g.width-1)*(gsw->pos-gsw->min))/(gsw->max-gsw->min); +} + +#if GINPUT_NEED_MOUSE + // A mouse up event + static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) { + #define gsw ((GSliderObject *)gw) + #define gh ((GHandle)gw) + + #if GWIN_BUTTON_LAZY_RELEASE + // Clip to the slider + if (x < 0) x = 0; + else if (x >= gh->width) x = gh->width-1; + if (y < 0) y = 0; + else if (y >= gh->height) x = gh->height-1; + #else + // Are we over the slider? + if (x < 0 || x >= gh->width || y < 0 || y >= gh->height) { + // No - restore the slider + ResetDisplayPos(gsw); + _gwinUpdate(gh); + return; + } + #endif + + // Set the new position + if (gh->width < gh->height) { + if (y > gh->height-GWIN_SLIDER_DEAD_BAND) + gsw->pos = gsw->min; + else if (y < GWIN_SLIDER_DEAD_BAND) + gsw->pos = gsw->max; + else + gsw->pos = (uint16_t)((int32_t)(gh->height-1-y-GWIN_SLIDER_DEAD_BAND)*(gsw->max-gsw->min)/(gh->height-2*GWIN_SLIDER_DEAD_BAND) + gsw->min); + } else { + if (x > gh->width-GWIN_SLIDER_DEAD_BAND) + gsw->pos = gsw->max; + else if (x < GWIN_SLIDER_DEAD_BAND) + gsw->pos = gsw->min; + else + gsw->pos = (uint16_t)((int32_t)(x-GWIN_SLIDER_DEAD_BAND)*(gsw->max-gsw->min)/(gh->width-2*GWIN_SLIDER_DEAD_BAND) + gsw->min); + } + + ResetDisplayPos(gsw); + _gwinUpdate(gh); + + // Generate the event + SendSliderEvent(gw); + #undef gh + #undef gsw + } + + // A mouse move (or mouse down) event + static void MouseMove(GWidgetObject *gw, coord_t x, coord_t y) { + #define gsw ((GSliderObject *)gw) + + // Determine the temporary display position (with range checking) + if (gw->g.width < gw->g.height) { + if (y < 0) + gsw->dpos = 0; + else if (y >= gw->g.height) + gsw->dpos = gw->g.height-1; + else + gsw->dpos = y; + } else { + if (x < 0) + gsw->dpos = 0; + else if (x >= gw->g.width) + gsw->dpos = gw->g.width-1; + else + gsw->dpos = x; + } + + // Update the display + _gwinUpdate(&gw->g); + #undef gsw + } +#endif + +#if GINPUT_NEED_TOGGLE + // A toggle on has occurred + static void ToggleOn(GWidgetObject *gw, uint16_t role) { + #define gsw ((GSliderObject *)gw) + + if (role) { + gwinSliderSetPosition((GHandle)gw, gsw->pos+(gsw->max-gsw->min)/GWIN_SLIDER_TOGGLE_INC); + SendSliderEvent(gw); + } else { + gwinSliderSetPosition((GHandle)gw, gsw->pos-(gsw->max-gsw->min)/GWIN_SLIDER_TOGGLE_INC); + SendSliderEvent(gw); + } + #undef gsw + } + + static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { + if (role) + ((GSliderObject *)gw)->t_up = instance; + else + ((GSliderObject *)gw)->t_dn = instance; + } + + static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { + return role ? ((GSliderObject *)gw)->t_up : ((GSliderObject *)gw)->t_dn; + } +#endif + +#if GINPUT_NEED_DIAL + // A dial move event + static void DialMove(GWidgetObject *gw, uint16_t role, uint16_t value, uint16_t max) { + #define gsw ((GSliderObject *)gw) + (void) role; + + // Set the new position + gsw->pos = (uint16_t)((uint32_t)value*(gsw->max-gsw->min)/max + gsw->min); + + ResetDisplayPos(gsw); + _gwinUpdate((GHandle)gw); + + // Generate the event + SendSliderEvent(gw); + #undef gsw + } + + static void DialAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { + (void) role; + ((GSliderObject *)gw)->dial = instance; + } + + static uint16_t DialGet(GWidgetObject *gw, uint16_t role) { + (void) role; + return ((GSliderObject *)gw)->dial; + } +#endif + +// The slider VMT table +static const gwidgetVMT sliderVMT = { + { + "Slider", // The classname + sizeof(GSliderObject), // The object size + _gwidgetDestroy, // The destroy routine + _gwidgetRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinSliderDraw_Std, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + 0, // Process mouse down events (NOT USED) + MouseUp, // Process mouse up events + MouseMove, // Process mouse move events + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 2, // 1 toggle role + ToggleAssign, // Assign Toggles + ToggleGet, // Get Toggles + 0, // Process toggle off events (NOT USED) + ToggleOn, // Process toggle on events + }, + #endif + #if GINPUT_NEED_DIAL + { + 1, // 1 dial roles + DialAssign, // Assign Dials + DialGet, // Get Dials + DialMove, // Process dial move events + }, + #endif +}; + +GHandle gwinGSliderCreate(GDisplay *g, GSliderObject *gs, const GWidgetInit *pInit) { + if (!(gs = (GSliderObject *)_gwidgetCreate(g, &gs->w, pInit, &sliderVMT))) + return 0; + #if GINPUT_NEED_TOGGLE + gs->t_dn = GWIDGET_NO_INSTANCE; + gs->t_up = GWIDGET_NO_INSTANCE; + #endif + #if GINPUT_NEED_DIAL + gs->dial = GWIDGET_NO_INSTANCE; + #endif + gs->min = 0; + gs->max = 100; + gs->pos = 0; + ResetDisplayPos(gs); + gwinSetVisible((GHandle)gs, pInit->g.show); + return (GHandle)gs; +} + +void gwinSliderSetRange(GHandle gh, int min, int max) { + #define gsw ((GSliderObject *)gh) + + if (gh->vmt != (gwinVMT *)&sliderVMT) + return; + + if (min == max) // prevent divide by 0 errors. + max++; + gsw->min = min; + gsw->max = max; + gsw->pos = min; + ResetDisplayPos(gsw); + #undef gsw +} + +void gwinSliderSetPosition(GHandle gh, int pos) { + #define gsw ((GSliderObject *)gh) + + if (gh->vmt != (gwinVMT *)&sliderVMT) + return; + + if (gsw->min <= gsw->max) { + if (pos < gsw->min) gsw->pos = gsw->min; + else if (pos > gsw->max) gsw->pos = gsw->max; + else gsw->pos = pos; + } else { + if (pos > gsw->min) gsw->pos = gsw->min; + else if (pos < gsw->max) gsw->pos = gsw->max; + else gsw->pos = pos; + } + ResetDisplayPos(gsw); + _gwinUpdate(gh); + + #undef gsw +} + +/*---------------------------------------------------------- + * Custom Draw Routines + *----------------------------------------------------------*/ + +void gwinSliderDraw_Std(GWidgetObject *gw, void *param) { + #define gsw ((GSliderObject *)gw) + const GColorSet * pcol; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&sliderVMT) + return; + + if ((gw->g.flags & GWIN_FLG_SYSENABLED)) + pcol = &gw->pstyle->pressed; + else + pcol = &gw->pstyle->disabled; + + if (gw->g.width < gw->g.height) { // Vertical slider + if (gsw->dpos != gw->g.height-1) + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.width, gw->g.height - gsw->dpos, pcol->progress); // Active Area + if (gsw->dpos != 0) + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, gw->pstyle->enabled.progress); // Inactive area + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb + if (gsw->dpos >= 2) + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos-2, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos-2, pcol->edge); // Thumb + if (gsw->dpos <= gw->g.height-2) + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos+2, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos+2, pcol->edge); // Thumb + + // Horizontal slider + } else { + if (gsw->dpos != gw->g.width-1) + gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.width-gsw->dpos, gw->g.height, gw->pstyle->enabled.progress); // Inactive area + if (gsw->dpos != 0) + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gsw->dpos, gw->g.height, pcol->progress); // Active Area + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge + gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb + if (gsw->dpos >= 2) + gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos-2, gw->g.y, gw->g.x+gsw->dpos-2, gw->g.y+gw->g.height-1, pcol->edge); // Thumb + if (gsw->dpos <= gw->g.width-2) + gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos+2, gw->g.y, gw->g.x+gsw->dpos+2, gw->g.y+gw->g.height-1, pcol->edge); // Thumb + } + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + + #undef gsw +} + +#if GDISP_NEED_IMAGE +void gwinSliderDraw_Image(GWidgetObject *gw, void *param) { + #define gsw ((GSliderObject *)gw) + #define gi ((gdispImage *)param) + const GColorSet * pcol; + coord_t z, v; + + if (gw->g.vmt != (gwinVMT *)&sliderVMT) + return; + + if ((gw->g.flags & GWIN_FLG_SYSENABLED)) + pcol = &gw->pstyle->pressed; + else + pcol = &gw->pstyle->disabled; + + if (gw->g.width < gw->g.height) { // Vertical slider + if (gsw->dpos != 0) // The unfilled area + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, gw->pstyle->enabled.progress); // Inactive area + if (gsw->dpos != gw->g.height-1) { // The filled area + for(z=gw->g.height, v=gi->height; z > gsw->dpos;) { + z -= v; + if (z < gsw->dpos) { + v -= gsw->dpos - z; + z = gsw->dpos; + } + gdispGImageDraw(gw->g.display, gi, gw->g.x, gw->g.y+z, gw->g.width, v, 0, gi->height-v); + } + } + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb + + // Horizontal slider + } else { + if (gsw->dpos != gw->g.width-1) // The unfilled area + gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.width-gsw->dpos, gw->g.height, gw->pstyle->enabled.progress); // Inactive area + if (gsw->dpos != 0) { // The filled area + for(z=0, v=gi->width; z < gsw->dpos; z += v) { + if (z+v > gsw->dpos) + v -= z+v - gsw->dpos; + gdispGImageDraw(gw->g.display, gi, gw->g.x+z, gw->g.y, v, gw->g.height, 0, 0); + } + } + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge + gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb + } + gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); + + #undef gsw +} +#endif /* GDISP_NEED_IMAGE */ + +#endif /* GFX_USE_GWIN && GWIN_NEED_BUTTON */ diff --git a/src/gwin/gwin_slider.h b/src/gwin/gwin_slider.h new file mode 100644 index 00000000..b88d6dfd --- /dev/null +++ b/src/gwin/gwin_slider.h @@ -0,0 +1,157 @@ +/* + * 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_slider.h + * @brief GWIN Graphic window subsystem header file. + * + * @defgroup Slider Slider + * @ingroup Widgets + * + * @details Create sliders with different styles + * + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_SLIDER must be set to TRUE in your gfxconf.h + * @{ + */ + +#ifndef _GWIN_SLIDER_H +#define _GWIN_SLIDER_H + +/* This file is included within "src/gwin/gwin_widget.h" */ + +#define GEVENT_GWIN_SLIDER (GEVENT_GWIN_CTRL_FIRST+1) + +typedef struct GEventGWinSlider { + GEventType type; // The type of this event (GEVENT_GWIN_BUTTON) + GHandle gwin; // The slider that is returning results + #if GWIN_WIDGET_TAGS + WidgetTag tag; // The slider tag + #endif + int position; +} GEventGWinSlider; + +// There are currently no GEventGWinSlider listening flags - use 0 + +// A slider window +typedef struct GSliderObject { + GWidgetObject w; + #if GINPUT_NEED_TOGGLE + uint16_t t_dn; + uint16_t t_up; + #endif + #if GINPUT_NEED_DIAL + uint16_t dial; + #endif + coord_t dpos; + int min; + int max; + int pos; +} GSliderObject; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a slider 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] gb The GSliderObject structure to initialise. If this is NULL the structure is dynamically allocated. + * @param[in] pInit The initialization parameters to use + * + * @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 slider remembers its normal drawing state. If there is a window manager then it is automatically + * redrawn if the window is moved or its visibility state is changed. + * @note The initial slider range is from 0 to 100 with an initial position of 0. + * @note A slider supports mouse, toggle and dial input. + * @note When assigning a toggle, only one toggle is supported per role. If you try to assign more than + * one toggle to a role it will forget the previous toggle. Two roles are supported: + * Role 0 = toggle for down, Role 1 = toggle for up. + * @note When assigning a dial, only one dial is supported. If you try to assign more than one dial + * it will forget the previous dial. Only dial role 0 is supported. + * + * @api + */ +GHandle gwinGSliderCreate(GDisplay *g, GSliderObject *gb, const GWidgetInit *pInit); +#define gwinSliderCreate(w, pInit) gwinGSliderCreate(GDISP, w, pInit) + +/** + * @brief Set the slider range. + * + * @param[in] gh The window handle (must be a slider window) + * @param[in] min The minimum value + * @param[in] max The maximum value + * @note Sets the position to the minimum value. + * @note The slider is not automatically drawn. Call gwinSliderDraw() after changing the range. + * + * @api + */ +void gwinSliderSetRange(GHandle gh, int min, int max); + +/** + * @brief Set the slider position. + * + * @param[in] gh The window handle (must be a slider window) + * @param[in] pos The new position + * @note If the new position is outside the slider range then the position + * is set to the closest end of the range. + * @note The slider is not automatically drawn. Call gwinSliderDraw() after changing the position. + * + * @api + */ +void gwinSliderSetPosition(GHandle gh, int pos); + +/** + * @brief Get the current slider position. + * @return The slider position + * + * @param[in] gh The window handle (must be a slider window) + * + * @note The use of a listener to get the slider position is recommended if you + * want continuous updates on the slider position. + * + * @api + */ +#define gwinSliderGetPosition(gh) (((GSliderObject *)(gh))->pos) + +/** + * @brief Some custom slider drawing routines + * @details These function may be passed to @p gwinSetCustomDraw() to get different slider drawing styles + * + * @param[in] gw The widget (which must be a slider) + * @param[in] param A parameter passed in from the user + * + * @note In your custom slider drawing function you may optionally call this + * standard functions and then draw your extra details on top. + * @note The standard functions below ignore the param parameter except for @p gwinSliderDraw_Image(). + * @note The image custom draw function @p gwinSliderDraw_Image() uses param to pass in the gdispImage pointer. + * The image must be already opened before calling @p gwinSetCustomDraw(). The image is tiled to fill + * the active area of the slider. The normal colors apply to the border and inactive area and the dividing line + * between the active and inactive areas. + * No checking is done to compare the dimensions of the slider to the size of the image. + * Note text is drawn on top of the image. + * @note These custom drawing routines don't have to worry about setting clipping as the framework + * sets clipping to the object window prior to calling these routines. + * + * @api + * @{ + */ +void gwinSliderDraw_Std(GWidgetObject *gw, void *param); +void gwinSliderDraw_Image(GWidgetObject *gw, void *param); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GWIN_SLIDER_H */ +/** @} */ diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c new file mode 100644 index 00000000..f85e6f20 --- /dev/null +++ b/src/gwin/gwin_widget.c @@ -0,0 +1,521 @@ +/* + * 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_widget.c + * @brief GWIN sub-system widget code + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_WIDGET + +#include + +#include "gwin_class.h" + +/* Our listener for events for widgets */ +static GListener gl; + +/* Our default style - a white background theme */ +const GWidgetStyle WhiteWidgetStyle = { + HTML2COLOR(0xFFFFFF), // window background + + // enabled color set + { + HTML2COLOR(0x000000), // text + HTML2COLOR(0x404040), // edge + HTML2COLOR(0xE0E0E0), // fill + HTML2COLOR(0xE0E0E0), // progress - inactive area + }, + + // disabled color set + { + HTML2COLOR(0xC0C0C0), // text + HTML2COLOR(0x808080), // edge + HTML2COLOR(0xE0E0E0), // fill + HTML2COLOR(0xC0E0C0), // progress - active area + }, + + // pressed color set + { + HTML2COLOR(0x404040), // text + HTML2COLOR(0x404040), // edge + HTML2COLOR(0x808080), // fill + HTML2COLOR(0x00E000), // progress - active area + }, +}; + +/* Our black style */ +const GWidgetStyle BlackWidgetStyle = { + HTML2COLOR(0x000000), // window background + + // enabled color set + { + HTML2COLOR(0xC0C0C0), // text + HTML2COLOR(0xC0C0C0), // edge + HTML2COLOR(0x606060), // fill + HTML2COLOR(0x404040), // progress - inactive area + }, + + // disabled color set + { + HTML2COLOR(0x808080), // text + HTML2COLOR(0x404040), // edge + HTML2COLOR(0x404040), // fill + HTML2COLOR(0x004000), // progress - active area + }, + + // pressed color set + { + HTML2COLOR(0xFFFFFF), // text + HTML2COLOR(0xC0C0C0), // edge + HTML2COLOR(0xE0E0E0), // fill + HTML2COLOR(0x008000), // progress - active area + }, +}; + +static const GWidgetStyle * defaultStyle = &BlackWidgetStyle; + +/* We use these everywhere in this file */ +#define gw ((GWidgetObject *)gh) +#define wvmt ((gwidgetVMT *)gh->vmt) + +/* Process an event */ +static void gwidgetEvent(void *param, GEvent *pe) { + #define pme ((GEventMouse *)pe) + #define pte ((GEventToggle *)pe) + #define pde ((GEventDial *)pe) + + GHandle h; + GHandle gh; + #if GFX_USE_GINPUT && (GINPUT_NEED_TOGGLE || GINPUT_NEED_DIAL) + uint16_t role; + #endif + (void) param; + + // Process various events + switch (pe->type) { + + #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE + case GEVENT_MOUSE: + case GEVENT_TOUCH: + // Cycle through all windows + for(gh = 0, h = gwinGetNextWindow(0); h; h = gwinGetNextWindow(h)) { + + // The window must be on this display and visible to be relevant + if (h->display != pme->display || !(h->flags & GWIN_FLG_SYSVISIBLE)) + continue; + + // Is the mouse currently captured by this widget? + if ((h->flags & (GWIN_FLG_WIDGET|GWIN_FLG_MOUSECAPTURE)) == (GWIN_FLG_WIDGET|GWIN_FLG_MOUSECAPTURE)) { + gh = h; + if ((pme->last_buttons & ~pme->current_buttons & GINPUT_MOUSE_BTN_LEFT)) { + gh->flags &= ~GWIN_FLG_MOUSECAPTURE; + if (wvmt->MouseUp) + wvmt->MouseUp(gw, pme->x - gh->x, pme->y - gh->y); + } else if (wvmt->MouseMove) + wvmt->MouseMove(gw, pme->x - gh->x, pme->y - gh->y); + + // There is only ever one captured mouse. Prevent normal mouse processing if there is a captured mouse + gh = 0; + break; + } + + // Save the highest z-order window that the mouse is over + if (pme->x >= h->x && pme->x < h->x + h->width && pme->y >= h->y && pme->y < h->y + h->height) + gh = h; + } + + // Process any mouse down over the highest order window if it is an enabled widget + if (gh && (gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) == (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) { + if ((~pme->last_buttons & pme->current_buttons & GINPUT_MOUSE_BTN_LEFT)) { + gh->flags |= GWIN_FLG_MOUSECAPTURE; + if (wvmt->MouseDown) + wvmt->MouseDown(gw, pme->x - gh->x, pme->y - gh->y); + } + } + break; + #endif + + #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE + case GEVENT_TOGGLE: + // Cycle through all windows + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + + // check if it a widget that is enabled and visible + if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) + continue; + + for(role = 0; role < wvmt->toggleroles; role++) { + if (wvmt->ToggleGet(gw, role) == pte->instance) { + if (pte->on) { + if (wvmt->ToggleOn) + wvmt->ToggleOn(gw, role); + } else { + if (wvmt->ToggleOff) + wvmt->ToggleOff(gw, role); + } + } + } + } + break; + #endif + + #if GFX_USE_GINPUT && GINPUT_NEED_DIAL + case GEVENT_DIAL: + // Cycle through all windows + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + + // check if it a widget that is enabled and visible + if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) + continue; + + for(role = 0; role < wvmt->dialroles; role++) { + if (wvmt->DialGet(gw, role) == pte->instance) { + if (wvmt->DialMove) + wvmt->DialMove(gw, role, pde->value, pde->maxvalue); + } + } + } + break; + #endif + + default: + break; + } + + #undef pme + #undef pte + #undef pde +} + +#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE + static GHandle FindToggleUser(uint16_t instance) { + GHandle gh; + uint16_t role; + + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if (!(gh->flags & GWIN_FLG_WIDGET)) // check if it a widget + continue; + + for(role = 0; role < wvmt->toggleroles; role++) { + if (wvmt->ToggleGet(gw, role) == instance) + return gh; + } + } + return 0; + } +#endif + +#if GFX_USE_GINPUT && GINPUT_NEED_DIAL + static GHandle FindDialUser(uint16_t instance) { + GHandle gh; + uint16_t role; + + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if (!(gh->flags & GWIN_FLG_WIDGET)) // check if it a widget + continue; + + for(role = 0; role < wvmt->dialroles; role++) { + if (wvmt->DialGet(gw, role) == instance) + return gh; + } + } + return 0; + } +#endif + +void _gwidgetInit(void) +{ + geventListenerInit(&gl); + geventRegisterCallback(&gl, gwidgetEvent, 0); +} + +void _gwidgetDeinit(void) +{ + /* ToDo */ +} + +GHandle _gwidgetCreate(GDisplay *g, GWidgetObject *pgw, const GWidgetInit *pInit, const gwidgetVMT *vmt) { + if (!(pgw = (GWidgetObject *)_gwindowCreate(g, &pgw->g, &pInit->g, &vmt->g, GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED))) + return 0; + + #if GWIN_NEED_COLLECTIONS + // This window can't be system enabled if the parent is not enabled + if (pgw->parent && !(pgw->parent->flags & GWIN_FLG_SYSENABLED)) + pgw->g.flags &= ~GWIN_FLG_SYSENABLED; + #endif + pgw->text = pInit->text ? pInit->text : ""; + pgw->fnDraw = pInit->customDraw ? pInit->customDraw : vmt->DefaultDraw; + pgw->fnParam = pInit->customParam; + pgw->pstyle = pInit->customStyle ? pInit->customStyle : defaultStyle; + #if GWIN_WIDGET_TAGS + pgw->tag = pInit->tag; + #endif + + return &pgw->g; +} + +void _gwidgetDestroy(GHandle gh) { + #if GFX_USE_GINPUT && (GINPUT_NEED_TOGGLE || GINPUT_NEED_DIAL) + uint16_t role, instance; + #endif + + // Deallocate the text (if necessary) + if ((gh->flags & GWIN_FLG_ALLOCTXT)) { + gh->flags &= ~GWIN_FLG_ALLOCTXT; + gfxFree((void *)gw->text); + } + + #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE + // Detach any toggles from this object + for(role = 0; role < wvmt->toggleroles; role++) { + instance = wvmt->ToggleGet(gw, role); + if (instance != GWIDGET_NO_INSTANCE) { + wvmt->ToggleAssign(gw, role, GWIDGET_NO_INSTANCE); + if (!FindToggleUser(instance)) + geventDetachSource(&gl, ginputGetToggle(instance)); + } + } + #endif + + #if GFX_USE_GINPUT && GINPUT_NEED_DIAL + // Detach any dials from this object + for(role = 0; role < wvmt->dialroles; role++) { + instance = wvmt->DialGet(gw, role); + if (instance != GWIDGET_NO_INSTANCE) { + wvmt->DialAssign(gw, role, GWIDGET_NO_INSTANCE); + if (!FindDialUser(instance)) + geventDetachSource(&gl, ginputGetDial(instance)); + } + } + #endif + + // Remove any listeners on this object. + geventDetachSourceListeners((GSourceHandle)gh); +} + +void _gwidgetRedraw(GHandle gh) { + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) + return; + + gw->fnDraw(gw, gw->fnParam); +} + +void _gwinSendEvent(GHandle gh, GEventType type) { + GSourceListener * psl; + GEventGWin * pge; + + // Trigger a GWIN Event + psl = 0; + while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { + if (!(pge = (GEventGWin *)geventGetEventBuffer(psl))) + continue; + pge->type = type; + pge->gwin = gh; + #if GWIN_WIDGET_TAGS + pge->tag = (gh->flags & GWIN_FLG_WIDGET) ? ((GWidgetObject *)gh)->tag : 0; + #endif + geventSendEvent(psl); + } +} + +void gwinWidgetClearInit(GWidgetInit *pwi) { + char *p; + unsigned len; + + for(p = (char *)pwi, len = sizeof(GWidgetInit); len; len--) + *p++ = 0; +} + +void gwinSetDefaultStyle(const GWidgetStyle *pstyle, bool_t updateAll) { + if (!pstyle) + pstyle = &BlackWidgetStyle; + + if (updateAll) { + GHandle gh; + + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if ((gh->flags & GWIN_FLG_WIDGET) && ((GWidgetObject *)gh)->pstyle == defaultStyle) + gwinSetStyle(gh, pstyle); + else + gwinRedraw(gh); + } + } + gwinSetDefaultBgColor(pstyle->background); + defaultStyle = pstyle; +} + +/** + * @brief Get the current default style. + * + * @api + */ +const GWidgetStyle *gwinGetDefaultStyle(void) { + return defaultStyle; +} + + +void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) { + if (!(gh->flags & GWIN_FLG_WIDGET)) + return; + + // Dispose of the old string + if ((gh->flags & GWIN_FLG_ALLOCTXT)) { + gh->flags &= ~GWIN_FLG_ALLOCTXT; + if (gw->text) { + gfxFree((void *)gw->text); + gw->text = ""; + } + } + + // Alloc the new text if required + if (!text || !*text) + gw->text = ""; + else if (useAlloc) { + char *str; + + if ((str = gfxAlloc(strlen(text)+1))) { + gh->flags |= GWIN_FLG_ALLOCTXT; + strcpy(str, text); + } + gw->text = (const char *)str; + } else + gw->text = text; + _gwinUpdate(gh); +} + +const char *gwinGetText(GHandle gh) { + if (!(gh->flags & GWIN_FLG_WIDGET)) + return 0; + + return gw->text; +} + +void gwinSetStyle(GHandle gh, const GWidgetStyle *pstyle) { + if (!(gh->flags & GWIN_FLG_WIDGET)) + return; + gw->pstyle = pstyle ? pstyle : defaultStyle; + gh->bgcolor = pstyle->background; + gh->color = pstyle->enabled.text; + _gwinUpdate(gh); +} + +const GWidgetStyle *gwinGetStyle(GHandle gh) { + if (!(gh->flags & GWIN_FLG_WIDGET)) + return 0; + + return gw->pstyle; +} + +void gwinSetCustomDraw(GHandle gh, CustomWidgetDrawFunction fn, void *param) { + if (!(gh->flags & GWIN_FLG_WIDGET)) + return; + + gw->fnDraw = fn ? fn : wvmt->DefaultDraw; + gw->fnParam = param; + _gwinUpdate(gh); +} + +bool_t gwinAttachListener(GListener *pl) { + return geventAttachSource(pl, GWIDGET_SOURCE, 0); +} + +#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE + bool_t gwinAttachMouse(uint16_t instance) { + GSourceHandle gsh; + + if (!(gsh = ginputGetMouse(instance))) + return FALSE; + + return geventAttachSource(&gl, gsh, GLISTEN_MOUSEMETA|GLISTEN_MOUSEDOWNMOVES); + } +#endif + +#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE + bool_t gwinAttachToggle(GHandle gh, uint16_t role, uint16_t instance) { + GSourceHandle gsh; + uint16_t oi; + + // Is this a widget + if (!(gh->flags & GWIN_FLG_WIDGET)) + return FALSE; + + // Is the role valid + if (role >= wvmt->toggleroles) + return FALSE; + + // Is this a valid device + if (!(gsh = ginputGetToggle(instance))) + return FALSE; + + // Is this already done? + oi = wvmt->ToggleGet(gw, role); + if (instance == oi) + return TRUE; + + // Remove the old instance + if (oi != GWIDGET_NO_INSTANCE) { + wvmt->ToggleAssign(gw, role, GWIDGET_NO_INSTANCE); + if (!FindToggleUser(oi)) + geventDetachSource(&gl, ginputGetToggle(oi)); + } + + // Assign the new + wvmt->ToggleAssign(gw, role, instance); + return geventAttachSource(&gl, gsh, GLISTEN_TOGGLE_ON|GLISTEN_TOGGLE_OFF); + } +#endif + +#if GFX_USE_GINPUT && GINPUT_NEED_DIAL + bool_t gwinAttachDial(GHandle gh, uint16_t role, uint16_t instance) { + GSourceHandle gsh; + uint16_t oi; + + if (!(gh->flags & GWIN_FLG_WIDGET)) + return FALSE; + + // Is the role valid + if (role >= wvmt->dialroles) + return FALSE; + + // Is this a valid device + if (!(gsh = ginputGetDial(instance))) + return FALSE; + + // Is this already done? + oi = wvmt->DialGet(gw, role); + if (instance == oi) + return TRUE; + + // Remove the old instance + if (oi != GWIDGET_NO_INSTANCE) { + wvmt->DialAssign(gw, role, GWIDGET_NO_INSTANCE); + if (!FindDialUser(oi)) + geventDetachSource(&gl, ginputGetDial(oi)); + } + + // Assign the new + wvmt->DialAssign(gw, role, instance); + return geventAttachSource(&gl, gsh, 0); + } +#endif + +#if GWIN_WIDGET_TAGS + void gwinSetTag(GHandle gh, WidgetTag tag) { + if ((gh->flags & GWIN_FLG_WIDGET)) + gw->tag = tag; + } + + WidgetTag gwinGetTag(GHandle gh) { + return ((gh->flags & GWIN_FLG_WIDGET)) ? gw->tag : 0; + } +#endif + +#endif /* GFX_USE_GWIN && GWIN_NEED_WIDGET */ +/** @} */ diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h new file mode 100644 index 00000000..81c76263 --- /dev/null +++ b/src/gwin/gwin_widget.h @@ -0,0 +1,391 @@ +/* + * 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_widget.h + * @brief GWIN Widgets header file. + * + * @defgroup Widget Widget + * @ingroup Widgets + * + * @details A widget is a Window that supports interacting with the user + * via an input device such as a mouse or toggle buttons. It is the + * base class for widgets such as buttons and sliders. + * + * @pre GFX_USE_GWIN and GWIN_NEED_WIDGET must be set to TRUE in your gfxconf.h + * @{ + */ + +#ifndef _GWIDGET_H +#define _GWIDGET_H + +/* This file is included within "src/gwin/sys_defs.h" */ + +// Forward definition +struct GWidgetObject; + +/** + * @brief The GColorSet structure + * @{ + */ +typedef struct GColorSet { + color_t text; // @< The text color + color_t edge; // @< The edge color + color_t fill; // @< The fill color + color_t progress; // @< The color of progress bars +} GColorSet; +/** @} */ + +/** + * @brief The GWidgetStyle structure + * @details A GWidgetStyle is a set of colors that together form a "style". + * These colors should not be confused with the GWindow foreground + * and background colors which are used for drawing operations. + * @{ + */ +typedef struct GWidgetStyle { + color_t background; // @< The window background color + GColorSet enabled; // @< The colors when enabled + GColorSet disabled; // @< The colors when disabled + GColorSet pressed; // @< The colors when pressed +} GWidgetStyle; +/** @} */ + +/** + * @brief We define a couple of GWidgetStyle's that you can use in your + * application. The Black style is the default style if you don't + * specify one. + * @note BlackWidgetStyle means that it is designed for a Black background. + * Similarly WhiteWidgetStyle is designed for a White background. + * @{ + */ +extern const GWidgetStyle BlackWidgetStyle; +extern const GWidgetStyle WhiteWidgetStyle; +/** @} */ + +/** + * @brief Defines a custom drawing function for a widget + */ +typedef void (*CustomWidgetDrawFunction)(struct GWidgetObject *gw, void *param); + +/** + * @brief Defines a the type of a tag on a widget + */ +typedef uint16_t WidgetTag; + +/** + * @brief The structure to initialise a widget. + * + * @note Some widgets may have extra parameters. + * @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 gwinWidgetClearInit() + * to clear it. + * @note The text element must be static string (not stack allocated). If you want to use + * a dynamic string (eg a stack allocated string) use NULL for this member and then call + * @p gwinSetText() with useAlloc set to TRUE. + * + * @{ + */ +typedef struct GWidgetInit { + GWindowInit g; // @< The GWIN initializer + const char * text; // @< The initial text + CustomWidgetDrawFunction customDraw; // @< A custom draw function - use NULL for the standard + void * customParam; // @< A parameter for the custom draw function (default = NULL) + const GWidgetStyle * customStyle; // @< A custom style to use - use NULL for the default style + #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) + WidgetTag tag; // @< The tag to associate with the widget + #endif +} GWidgetInit; +/** @} */ + +/** + * @brief The GWIN Widget structure + * @note A widget is a GWIN window that accepts user input. + * It also has a number of other properties such as its ability + * to redraw itself (a widget maintains drawing state). + * @note Do not access the members directly. Treat it as a black-box and use the method functions. + * + * @{ + */ +typedef struct GWidgetObject { + GWindowObject g; // @< This is still a GWIN + const char * text; // @< The widget text + CustomWidgetDrawFunction fnDraw; // @< The current draw function + void * fnParam; // @< A parameter for the current draw function + const GWidgetStyle * pstyle; // @< The current widget style colors + #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) + WidgetTag tag; // @< The widget tag + #endif +} GWidgetObject; +/** @} */ + +/** + * A comment/rant on the above structure: + * We would really like the GWindowObject member to be anonymous. While this is + * allowed under the C11, C99, GNU and various other standards which have been + * around forever - compiler support often requires special flags e.g + * gcc requires the -fms-extensions flag (no wonder the language and compilers have + * not really progressed in 30 years). As portability is a key requirement + * we unfortunately won't use this useful feature in case we get a compiler that + * won't support it even with special flags. + */ + +/** + * @brief A Generic GWIN Event + * @note All gwin windows when sending events will either use this structure or a + * structure that is 100% compatible except that it may also have extra fields. + * @note There are currently no GEventGWin listening flags - use 0 as the flags to @p gwinAttachListener() + * + * @{ + */ +typedef struct GEventGWin { + GEventType type; // The type of this event + GHandle gwin; // The gwin window handle + #if GWIN_NEED_WIDGET && GWIN_WIDGET_TAGS + WidgetTag tag; // The tag (if applicable) + #endif +} GEventGWin; +/** @} */ + +/** + * @brief The list of predefined GWIN events. + * @note The definition of an event type does not mean it is always sent. For example, + * close events are sent by Frame windows but by little else. They are normally + * only sent if there is a specific reason that the event should be sent. + * @{ + */ +#define GEVENT_GWIN_OPEN (GEVENT_GWIN_FIRST+0x00) +#define GEVENT_GWIN_CLOSE (GEVENT_GWIN_FIRST+0x01) +#define GEVENT_GWIN_RESIZE (GEVENT_GWIN_FIRST+0x02) +#define GEVENT_GWIN_CTRL_FIRST (GEVENT_GWIN_FIRST+0x40) +/** @} */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Clear a GWidgetInit 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 GWidgetInit structure to clear + * + * @api + */ +void gwinWidgetClearInit(GWidgetInit *pwi); + +/** + * @brief Set the default style for widgets created hereafter. + * + * @param[in] pstyle The default style. Passing NULL uses the system compiled style. + * @param[in] updateAll If TRUE then all existing widgets that are using the current default style + * will be updated to use this new style. Widgets that have custom styles different + * from the default style will not be updated. + * + * @note The style must be allocated statically (not on the stack) as only the pointer is stored. + * + * @api + */ +void gwinSetDefaultStyle(const GWidgetStyle *pstyle, bool_t updateAll); + +/** + * @brief Get the current default style. + * + * @return The current default style. + * + * @api + */ +const GWidgetStyle *gwinGetDefaultStyle(void); + +/** + * @brief Set the text of a widget. + * + * @param[in] gh The widget handle + * @param[in] text The text to set. This must be a constant string unless useAlloc is set. + * @param[in] useAlloc If TRUE the string specified will be copied into dynamically allocated memory. + * + * @note The widget is automatically redrawn + * @note Non-widgets will ignore this call. + * + * @api + */ +void gwinSetText(GHandle gh, const char *text, bool_t useAlloc); + +/** + * @brief Get the text of a widget. + * @return The widget text or NULL if it isn't a widget + * + * @param[in] gh The widget handle + * + * @api + */ +const char *gwinGetText(GHandle gh); + +#if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) + /** + * @brief Set the tag of a widget. + * + * @param[in] gh The widget handle + * @param[in] tag The tag to set. + * + * @note Non-widgets will ignore this call. + * + * @pre Requires GWIN_WIDGET_TAGS to be TRUE + * + * @api + */ + void gwinSetTag(GHandle gh, WidgetTag tag); + + /** + * @brief Get the tag of a widget. + * @return The widget tag value (or 0 if it is not a widget) + * + * @param[in] gh The widget handle + * + * @pre Requires GWIN_WIDGET_TAGS to be TRUE + * + * @api + */ + WidgetTag gwinGetTag(GHandle gh); +#endif + +/** + * @brief Set the style of a widget. + * + * @param[in] gh The widget handle + * @param[in] pstyle The style to set. This must be a static structure (not allocated on a transient stack). + * Use NULL to reset to the default style. + * + * @note The widget is automatically redrawn + * @note Non-widgets will ignore this call. + * + * @api + */ +void gwinSetStyle(GHandle gh, const GWidgetStyle *pstyle); + +/** + * @brief Get the style of a widget. + * @return The widget style or NULL if it isn't a widget + * + * @param[in] gh The widget handle + * + * @api + */ +const GWidgetStyle *gwinGetStyle(GHandle gh); + +/** + * @brief Set the routine to perform a custom widget drawing. + * + * @param[in] gh The widget handle + * @param[in] fn The function to use to draw the widget + * @param[in] param A parameter to pass to the widget drawing function + * + * @note The widget is not automatically redrawn. Call @p gwinDraw() to redraw the widget. + * @note Non-widgets will ignore this call. + * + * @api + */ +void gwinSetCustomDraw(GHandle gh, CustomWidgetDrawFunction fn, void *param); + +/** + * @brief Attach a Listener to listen for widget events + * @return TRUE on success + * + * @param[in] pl The listener + * + * @api + */ +bool_t gwinAttachListener(GListener *pl); + +#if (GFX_USE_GINPUT && GINPUT_NEED_MOUSE) || defined(__DOXYGEN__) + /** + * @brief Set the mouse to be used to control the widgets + * @return TRUE on success + * + * @param[in] instance The mouse instance + * + * @note Every widget uses the same mouse. + * + * @api + */ + bool_t gwinAttachMouse(uint16_t instance); +#endif + +#if (GFX_USE_GINPUT && GINPUT_NEED_TOGGLE) || defined(__DOXYGEN__) + /** + * @brief Attach a toggle to a widget + * @return TRUE on success + * + * @param[in] gh The widget handle + * @param[in] role The function the toggle will perform for the widget + * @param[in] instance The toggle instance + * + * @note See the documentation on the specific widget to see the possible + * values for the role parameter. If it is out of range, this function + * will return FALSE + * + * @api + */ + bool_t gwinAttachToggle(GHandle gh, uint16_t role, uint16_t instance); +#endif + +#if (GFX_USE_GINPUT && GINPUT_NEED_DIAL) || defined(__DOXYGEN__) + /** + * @brief Attach a toggle to a widget + * @return TRUE on success + * + * @param[in] gh The widget handle + * @param[in] role The function the dial will perform for the widget + * @param[in] instance The dial instance + * + * @note See the documentation on the specific widget to see the possible + * values for the role parameter. If it is out of range, this function + * will return FALSE + * + * @api + */ + bool_t gwinAttachDial(GHandle gh, uint16_t role, uint16_t instance); +#endif + +#ifdef __cplusplus +} +#endif + +/* Include extra widget types */ +#if GWIN_NEED_BUTTON || defined(__DOXYGEN__) + #include "gwin_button.h" +#endif + +#if GWIN_NEED_SLIDER || defined(__DOXYGEN__) + #include "gwin_slider.h" +#endif + +#if GWIN_NEED_CHECKBOX || defined(__DOXYGEN__) + #include "gwin_checkbox.h" +#endif + +#if GWIN_NEED_RADIO || defined(__DOXYGEN__) + #include "gwin_radio.h" +#endif + +#if GWIN_NEED_LABEL || defined(__DOXYGEN__) + #include "gwin_label.h" +#endif + +#if GWIN_NEED_LIST || defined(__DOXYGEN__) + #include "gwin_list.h" +#endif + +#if GWIN_NEED_PROGRESSBAR || defined(__DOXYGEN__) + #include "gwin_progressbar.h" +#endif + +#endif /* _GWIDGET_H */ +/** @} */ diff --git a/src/gwin/gwin_wm.c b/src/gwin/gwin_wm.c new file mode 100644 index 00000000..92d68c7b --- /dev/null +++ b/src/gwin/gwin_wm.c @@ -0,0 +1,852 @@ +/* + * 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_wm.c + * @brief GWIN sub-system window manager code + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && !GWIN_NEED_WINDOWMANAGER + /** + * A really nasty default implementation for the simplest of systems + */ + + + #include "gwin_class.h" + + // Needed if there is no window manager + #define MIN_WIN_WIDTH 1 + #define MIN_WIN_HEIGHT 1 + + static gfxMutex gmutex; + + void _gwmInit(void) { + gfxMutexInit(&gmutex); + } + + void _gwmDeinit(void) { + gfxMutexDestroy(&gmutex); + } + + bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit) { + gh->x = gh->y = gh->width = gh->height = 0; + gwinMove(gh, pInit->x, pInit->y); + gwinResize(gh, pInit->width, pInit->height); + return TRUE; + } + + void _gwinFlushRedraws(GRedrawMethod how) { + (void) how; + + // We are always flushed + } + + + #if GDISP_NEED_CLIP + static void getLock(GHandle gh) { + gfxMutexEnter(&gmutex); + gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); + } + static void exitLock(GHandle gh) { + gdispGUnsetClip(gh->display); + gfxMutexExit(&gmutex); + } + #else + #define getLock(gh) gfxMutexEnter(&gmutex) + #define exitLock(gh) gfxMutexExit(&gmutex) + #endif + + void _gwinUpdate(GHandle gh) { + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { + if (gh->vmt->Redraw) { + getLock(gh); + gh->vmt->Redraw(gh); + exitLock(gh); + } else if ((gh->flags & GWIN_FLG_BGREDRAW)) { + getLock(gh); + gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); + exitLock(gh); + if (gh->vmt->AfterClear) + gh->vmt->AfterClear(gh); + } + } else if ((gh->flags & GWIN_FLG_BGREDRAW)) { + getLock(gh); + gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor()); + exitLock(gh); + } + gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + } + + bool_t _gwinDrawStart(GHandle gh) { + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) + return FALSE; + + getLock(gh); + return TRUE; + } + + void _gwinDrawEnd(GHandle gh) { + (void) gh; + exitLock(gh); + } + + void gwinSetVisible(GHandle gh, bool_t visible) { + if (visible) { + if (!(gh->flags & GWIN_FLG_VISIBLE)) { + gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_BGREDRAW); + _gwinUpdate(gh); + } + } else { + if ((gh->flags & GWIN_FLG_VISIBLE)) { + gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE); + gh->flags |= GWIN_FLG_BGREDRAW; + _gwinUpdate(gh); + } + } + } + + void gwinSetEnabled(GHandle gh, bool_t enabled) { + if (enabled) { + if (!(gh->flags & GWIN_FLG_ENABLED)) { + gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); + _gwinUpdate(gh); + } + } else { + if ((gh->flags & GWIN_FLG_ENABLED)) { + gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); + _gwinUpdate(gh); + } + } + } + + void gwinMove(GHandle gh, coord_t x, coord_t y) { + gh->x = x; gh->y = y; + if (gh->x < 0) gh->x = 0; + if (gh->y < 0) gh->y = 0; + if (gh->x > gdispGGetWidth(gh->display)-MIN_WIN_WIDTH) gh->x = gdispGGetWidth(gh->display)-MIN_WIN_WIDTH; + if (gh->y > gdispGGetHeight(gh->display)-MIN_WIN_HEIGHT) gh->y = gdispGGetHeight(gh->display)-MIN_WIN_HEIGHT; + if (gh->x+gh->width > gdispGGetWidth(gh->display)) gh->width = gdispGGetWidth(gh->display) - gh->x; + if (gh->y+gh->height > gdispGGetHeight(gh->display)) gh->height = gdispGGetHeight(gh->display) - gh->y; + _gwinUpdate(gh); + } + + void gwinResize(GHandle gh, coord_t width, coord_t height) { + gh->width = width; gh->height = height; + if (gh->width < MIN_WIN_WIDTH) { gh->width = MIN_WIN_WIDTH; } + if (gh->height < MIN_WIN_HEIGHT) { gh->height = MIN_WIN_HEIGHT; } + if (gh->x+gh->width > gdispGGetWidth(gh->display)) gh->width = gdispGGetWidth(gh->display) - gh->x; + if (gh->y+gh->height > gdispGGetHeight(gh->display)) gh->height = gdispGGetHeight(gh->display) - gh->y; + _gwinUpdate(gh); + } + + void gwinRedraw(GHandle gh) { + _gwinUpdate(gh); + } +#endif + +#if GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER + +#include "gwin_class.h" + +/*----------------------------------------------- + * Data + *-----------------------------------------------*/ + +// The default window manager +extern const GWindowManager GNullWindowManager; +GWindowManager * _GWINwm; + +static gfxSem gwinsem; +static gfxQueueASync _GWINList; +#if !GWIN_REDRAW_IMMEDIATE + static GTimer RedrawTimer; + static void RedrawTimerFn(void *param); +#endif +static volatile uint8_t RedrawPending; + #define DOREDRAW_INVISIBLES 0x01 + #define DOREDRAW_VISIBLES 0x02 + + +/*----------------------------------------------- + * Window Routines + *-----------------------------------------------*/ + +void _gwmInit(void) +{ + gfxSemInit(&gwinsem, 1, 1); + gfxQueueASyncInit(&_GWINList); + #if !GWIN_REDRAW_IMMEDIATE + gtimerInit(&RedrawTimer); + gtimerStart(&RedrawTimer, RedrawTimerFn, 0, TRUE, TIME_INFINITE); + #endif + _GWINwm = (GWindowManager *)&GNullWindowManager; + _GWINwm->vmt->Init(); +} + +void _gwmDeinit(void) +{ + GHandle gh; + + while((gh = gwinGetNextWindow(0))) + gwinDestroy(gh); + + _GWINwm->vmt->DeInit(); + #if !GWIN_REDRAW_IMMEDIATE + gtimerDeinit(&RedrawTimer); + #endif + gfxQueueASyncDeinit(&_GWINList); + gfxSemDestroy(&gwinsem); +} + +#if GWIN_REDRAW_IMMEDIATE + #define TriggerRedraw(void) _gwinFlushRedraws(REDRAW_NOWAIT); +#else + #define TriggerRedraw() gtimerJab(&RedrawTimer); + + static void RedrawTimerFn(void *param) { + (void) param; + _gwinFlushRedraws(REDRAW_NOWAIT); + } +#endif + +void _gwinFlushRedraws(GRedrawMethod how) { + GHandle gh; + + // Do we really need to do anything? + if (!RedrawPending) + return; + + // Obtain the drawing lock + if (how == REDRAW_WAIT) + gfxSemWait(&gwinsem, TIME_INFINITE); + else if (how == REDRAW_NOWAIT && !gfxSemWait(&gwinsem, TIME_IMMEDIATE)) + // Someone is drawing - They will do the redraw when they are finished + return; + + // Do loss of visibility first + while ((RedrawPending & DOREDRAW_INVISIBLES)) { + RedrawPending &= ~DOREDRAW_INVISIBLES; // Catch new requests + + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) != GWIN_FLG_NEEDREDRAW) + continue; + + // Do the redraw + #if GDISP_NEED_CLIP + gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); + _GWINwm->vmt->Redraw(gh); + gdispGUnsetClip(gh->display); + #else + _GWINwm->vmt->Redraw(gh); + #endif + + // Postpone further redraws + #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP + if (how == REDRAW_NOWAIT) { + RedrawPending |= DOREDRAW_INVISIBLES; + TriggerRedraw(); + goto releaselock; + } + #endif + } + } + + // Do the visible windows next + while ((RedrawPending & DOREDRAW_VISIBLES)) { + RedrawPending &= ~DOREDRAW_VISIBLES; // Catch new requests + + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) + continue; + + // Do the redraw + #if GDISP_NEED_CLIP + gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); + _GWINwm->vmt->Redraw(gh); + gdispGUnsetClip(gh->display); + #else + _GWINwm->vmt->Redraw(gh); + #endif + + // Postpone further redraws (if there are any and the options are set right) + #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP + if (how == REDRAW_NOWAIT) { + while((gh = gwinGetNextWindow(gh))) { + if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) { + RedrawPending |= DOREDRAW_VISIBLES; + TriggerRedraw(); + break; + } + } + goto releaselock; + } + #endif + } + } + + #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP + releaselock: + #endif + + // Release the lock + if (how == REDRAW_WAIT || how == REDRAW_NOWAIT) + gfxSemSignal(&gwinsem); +} + +void _gwinUpdate(GHandle gh) { + // Only redraw if visible + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) + return; + + // Mark for redraw + gh->flags |= GWIN_FLG_NEEDREDRAW; + RedrawPending |= DOREDRAW_VISIBLES; + + // Asynchronous redraw + TriggerRedraw(); +} + +bool_t _gwinDrawStart(GHandle gh) { + // This test should occur inside the lock. We do this + // here as well as an early out (more efficient). + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) + return FALSE; + + // Obtain the drawing lock + gfxSemWait(&gwinsem, TIME_INFINITE); + + // Re-test visibility as we may have waited a while + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) { + _gwinDrawEnd(gh); + return FALSE; + } + + // OK - we are ready to draw. + #if GDISP_NEED_CLIP + gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); + #endif + return TRUE; +} + +void _gwinDrawEnd(GHandle gh) { + // Ensure there is no clip set + #if GDISP_NEED_CLIP + gdispGUnsetClip(gh->display); + #endif + + // Look for something to redraw + _gwinFlushRedraws(REDRAW_INSESSION); + + // Release the lock + gfxSemSignal(&gwinsem); +} + +bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit) { + #if GWIN_NEED_CONTAINERS + // Save the parent + gh->parent = pInit->parent; + + // Ensure the display is consistent with any parents + if (gh->parent && (!(gh->parent->flags & GWIN_FLG_CONTAINER) || gh->display != gh->parent->display)) + return FALSE; + #endif + + // Add to the window manager + if (!_GWINwm->vmt->Add(gh, pInit)) + return FALSE; + + #if GWIN_NEED_CONTAINERS + // Notify the parent it has been added + if (gh->parent && ((gcontainerVMT *)gh->parent->vmt)->NotifyAdd) + ((gcontainerVMT *)gh->parent->vmt)->NotifyAdd(gh->parent, gh); + #endif + + return TRUE; +} + +void gwinSetWindowManager(struct GWindowManager *gwm) { + if (!gwm) + gwm = (GWindowManager *)&GNullWindowManager; + if (_GWINwm != gwm) { + _GWINwm->vmt->DeInit(); + _GWINwm = gwm; + _GWINwm->vmt->Init(); + } +} + +void gwinRedraw(GHandle gh) { + // Only redraw if visible + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) + return; + + // Mark for redraw + gh->flags |= GWIN_FLG_NEEDREDRAW; + RedrawPending |= DOREDRAW_VISIBLES; + + // Synchronous redraw + _gwinFlushRedraws(REDRAW_WAIT); +} + +#if GWIN_NEED_CONTAINERS + void gwinSetVisible(GHandle gh, bool_t visible) { + if (visible) { + // Mark us as visible + gh->flags |= GWIN_FLG_VISIBLE; + + // Do we want to be added to the display + if (!(gh->flags & GWIN_FLG_SYSVISIBLE) && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE))) { + // Check each window's visibility is consistent with its parents + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if ((gh->flags & (GWIN_FLG_SYSVISIBLE|GWIN_FLG_VISIBLE)) == GWIN_FLG_VISIBLE && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE))) + gh->flags |= (GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); // Fix it and mark for redraw + } + + // Mark for redraw + RedrawPending |= DOREDRAW_VISIBLES; + TriggerRedraw(); + } + } else { + // Mark us as not visible + gh->flags &= ~GWIN_FLG_VISIBLE; + + // Do we need to be removed from the display + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { + gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + + // Check each window's visibility is consistent with its parents + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if ((gh->flags & GWIN_FLG_SYSVISIBLE) && (!(gh->flags & GWIN_FLG_VISIBLE) || (gh->parent && !(gh->parent->flags & GWIN_FLG_SYSVISIBLE)))) + gh->flags &= ~GWIN_FLG_SYSVISIBLE; // Fix it + } + + // Mark for redraw - no need to redraw children + RedrawPending |= DOREDRAW_INVISIBLES; + TriggerRedraw(); + } + } + } +#else + void gwinSetVisible(GHandle gh, bool_t visible) { + if (visible) { + if (!(gh->flags & GWIN_FLG_VISIBLE)) { + gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + RedrawPending |= DOREDRAW_VISIBLES; + TriggerRedraw(); + } + } else { + if ((gh->flags & GWIN_FLG_VISIBLE)) { + gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE); + gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + RedrawPending |= DOREDRAW_INVISIBLES; + TriggerRedraw(); + } + } + } +#endif + +#if GWIN_NEED_CONTAINERS + // These two sub-functions set/clear system enable recursively. + void gwinSetEnabled(GHandle gh, bool_t enabled) { + if (enabled) { + // Mark us as enabled + gh->flags |= GWIN_FLG_ENABLED; + + // Do we change our real enabled state + if (!(gh->flags & GWIN_FLG_SYSENABLED) && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) { + // Check each window's enabled state is consistent with its parents + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if ((gh->flags & (GWIN_FLG_SYSENABLED|GWIN_FLG_ENABLED)) == GWIN_FLG_ENABLED && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) { + gh->flags |= GWIN_FLG_SYSENABLED; // Fix it + _gwinUpdate(gh); + } + } + } + } else { + gh->flags &= ~GWIN_FLG_ENABLED; + + // Do we need to change our real enabled state + if ((gh->flags & GWIN_FLG_SYSENABLED)) { + // Check each window's visibility is consistent with its parents + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + if ((gh->flags & GWIN_FLG_SYSENABLED) && (!(gh->flags & GWIN_FLG_ENABLED) || (gh->parent && !(gh->parent->flags & GWIN_FLG_SYSENABLED)))) { + gh->flags &= ~GWIN_FLG_SYSENABLED; // Fix it + _gwinUpdate(gh); + } + } + } + } + } +#else + void gwinSetEnabled(GHandle gh, bool_t enabled) { + if (enabled) { + if (!(gh->flags & GWIN_FLG_ENABLED)) { + gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); + _gwinUpdate(gh); + } + } else { + if ((gh->flags & GWIN_FLG_ENABLED)) { + gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); + _gwinUpdate(gh); + } + } + } +#endif + +void gwinMove(GHandle gh, coord_t x, coord_t y) { + _GWINwm->vmt->Move(gh, x, y); +} + +void gwinResize(GHandle gh, coord_t width, coord_t height) { + _GWINwm->vmt->Size(gh, width, height); +} + +void gwinSetMinMax(GHandle gh, GWindowMinMax minmax) { + _GWINwm->vmt->MinMax(gh, minmax); +} + +void gwinRaise(GHandle gh) { + _GWINwm->vmt->Raise(gh); +} + +GWindowMinMax gwinGetMinMax(GHandle gh) { + if (gh->flags & GWIN_FLG_MINIMIZED) + return GWIN_MINIMIZE; + if (gh->flags & GWIN_FLG_MAXIMIZED) + return GWIN_MAXIMIZE; + return GWIN_NORMAL; +} + +void gwinRedrawDisplay(GDisplay *g, bool_t preserve) { + GHandle gh; + + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + + // Skip if it is for a different display + if (g && gh->display != g) + continue; + + #if GWIN_NEED_CONTAINERS + // Skip if it is not a top level window (parents internally take care of their children) + if (gh->parent) + continue; + #endif + + // Only visible windows are to be redrawn + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) + continue; + + if (!preserve) + gh->flags |= GWIN_FLG_BGREDRAW; + + _gwinUpdate(gh); + } +} + +GHandle gwinGetNextWindow(GHandle gh) { + return gh ? (GHandle)gfxQueueASyncNext(&gh->wmq) : (GHandle)gfxQueueASyncPeek(&_GWINList); +} + +/*----------------------------------------------- + * "Null" Window Manager Routines + *-----------------------------------------------*/ + +// This is a parent reveal operation +#define GWIN_FLG_PARENTREVEAL (GWIN_FIRST_WM_FLAG << 0) + +// Minimum dimensions +#define MIN_WIN_WIDTH 3 +#define MIN_WIN_HEIGHT 3 + + +static void WM_Init(void); +static void WM_DeInit(void); +static bool_t WM_Add(GHandle gh, const GWindowInit *pInit); +static void WM_Delete(GHandle gh); +static void WM_Redraw(GHandle gh); +static void WM_Size(GHandle gh, coord_t w, coord_t h); +static void WM_Move(GHandle gh, coord_t x, coord_t y); +static void WM_Raise(GHandle gh); +static void WM_MinMax(GHandle gh, GWindowMinMax minmax); + +static const gwmVMT GNullWindowManagerVMT = { + WM_Init, + WM_DeInit, + WM_Add, + WM_Delete, + WM_Redraw, + WM_Size, + WM_Move, + WM_Raise, + WM_MinMax, +}; + +const GWindowManager GNullWindowManager = { + &GNullWindowManagerVMT, +}; + +static void WM_Init(void) { + // We don't need to do anything here. + // A full window manager would move the windows around, add borders etc + + // clear the screen + // cycle through the windows already defined displaying them + // or cut all the window areas out of the screen and clear the remainder +} + +static void WM_DeInit(void) { + // We don't need to do anything here. + // A full window manager would remove any borders etc +} + +static bool_t WM_Add(GHandle gh, const GWindowInit *pInit) { + // Note the window will not currently be marked as visible + + // Put it on the end of the queue + gfxQueueASyncPut(&_GWINList, &gh->wmq); + + // Make sure the size/position is valid - prefer position over size. + gh->width = MIN_WIN_WIDTH; gh->height = MIN_WIN_HEIGHT; + gh->x = gh->y = 0; + WM_Move(gh, pInit->x, pInit->y); + WM_Size(gh, pInit->width, pInit->height); + return TRUE; +} + +static void WM_Delete(GHandle gh) { + // Remove it from the window list + gfxQueueASyncRemove(&_GWINList, &gh->wmq); +} + +static void WM_Redraw(GHandle gh) { + #if GWIN_NEED_CONTAINERS + redo_redraw: + #endif + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { + if (gh->vmt->Redraw) + gh->vmt->Redraw(gh); + else if ((gh->flags & GWIN_FLG_BGREDRAW)) { + // We can't redraw but we want full coverage so just clear the area + gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); + + // Only do an after clear if this is not a parent reveal + if (!(gh->flags & GWIN_FLG_PARENTREVEAL) && gh->vmt->AfterClear) + gh->vmt->AfterClear(gh); + } + + // Redraw is done + gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL); + + #if GWIN_NEED_CONTAINERS + // If this is container but not a parent reveal, mark any visible children for redraw + // We redraw our children here as we have overwritten them in redrawing the parent + // as GDISP/GWIN doesn't yet support complex clipping regions. + if ((gh->flags & (GWIN_FLG_CONTAINER|GWIN_FLG_PARENTREVEAL)) == GWIN_FLG_CONTAINER) { + for(gh = gwinGetFirstChild(gh); gh; gh = gwinGetSibling(gh)) + _gwinUpdate(gh); + } + #endif + } else { + if ((gh->flags & GWIN_FLG_BGREDRAW)) { + GHandle gx; + + #if GWIN_NEED_CONTAINERS + if (gh->parent) { + // Child redraw is done + gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL); + + // Get the parent to redraw the area + gh = gh->parent; + gh->flags |= (GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL); + goto redo_redraw; + } + #endif + + // Clear the area to the background color + gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor()); + + // Now loop over all windows looking for overlaps. Redraw them if they overlap the newly exposed area. + for(gx = gwinGetNextWindow(0); gx; gx = gwinGetNextWindow(gx)) { + if ((gx->flags & GWIN_FLG_SYSVISIBLE) + && gx->display == gh->display + && gx->x < gh->x+gh->width && gx->y < gh->y+gh->height && gx->x+gx->width >= gh->x && gx->y+gx->height >= gh->y) { + if (gx->vmt->Redraw) + gx->vmt->Redraw(gx); + else + // We can't redraw this window but we want full coverage so just clear the area + gdispGFillArea(gx->display, gx->x, gx->y, gx->width, gx->height, gx->bgcolor); + } + } + } + + // Redraw is done + gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL); + } +} + +static void WM_Size(GHandle gh, coord_t w, coord_t h) { + coord_t v; + + #if GWIN_NEED_CONTAINERS + if (gh->parent) { + // Clip to the container + v = gh->parent->x + gh->parent->width - ((const gcontainerVMT *)gh->parent->vmt)->RightBorder(gh->parent); + if (gh->x+w > v) w = v - gh->x; + v = gh->parent->y + gh->parent->height - ((const gcontainerVMT *)gh->parent->vmt)->BottomBorder(gh->parent); + if (gh->y+h > v) h = v - gh->y; + } + #endif + + // Clip to the screen + v = gdispGGetWidth(gh->display); + if (gh->x+w > v) w = v - gh->x; + v = gdispGGetHeight(gh->display); + if (gh->y+h > v) h = v - gh->y; + + // Give it a minimum size + if (w < MIN_WIN_WIDTH) w = MIN_WIN_WIDTH; + if (h < MIN_WIN_HEIGHT) h = MIN_WIN_HEIGHT; + + // If there has been no resize just exit + if (gh->width == w && gh->height == h) + return; + + // Set the new size and redraw + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { + if (w >= gh->width && h >= gh->height) { + + // The new size is larger - just redraw + gh->width = w; gh->height = h; + _gwinUpdate(gh); + + } else { + // We need to make this window invisible and ensure that has been drawn + gwinSetVisible(gh, FALSE); + _gwinFlushRedraws(REDRAW_WAIT); + + // Resize + gh->width = w; gh->height = h; + + #if GWIN_NEED_CONTAINERS + // Any children outside the new area need to be moved + if ((gh->flags & GWIN_FLG_CONTAINER)) { + GHandle child; + + // Move to their old relative location. THe WM_Move() will adjust as necessary + for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child)) + WM_Move(gh, child->x-gh->x-((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent), child->y-gh->y-((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent)); + } + #endif + + // Mark it visible again in its new location + gwinSetVisible(gh, TRUE); + } + } else { + gh->width = w; gh->height = h; + + #if GWIN_NEED_CONTAINERS + // Any children outside the new area need to be moved + if ((gh->flags & GWIN_FLG_CONTAINER)) { + GHandle child; + + // Move to their old relative location. THe WM_Move() will adjust as necessary + for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child)) + WM_Move(gh, child->x-gh->x-((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent), child->y-gh->y-((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent)); + } + #endif + } +} + +static void WM_Move(GHandle gh, coord_t x, coord_t y) { + coord_t v; + + #if GWIN_NEED_CONTAINERS + if (gh->parent) { + // Clip to the parent size + v = gh->parent->width - ((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent) - ((const gcontainerVMT *)gh->parent->vmt)->RightBorder(gh->parent); + if (x+gh->width > v) x = v-gh->width; + v = gh->parent->height - ((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent) - ((const gcontainerVMT *)gh->parent->vmt)->BottomBorder(gh->parent); + if (y+gh->height > v) y = v-gh->height; + if (x < 0) x = 0; + if (y < 0) y = 0; + + // Convert to absolute position + x += gh->parent->x + ((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent); + y += gh->parent->y + ((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent); + } + #endif + + // Clip to the screen + v = gdispGGetWidth(gh->display); + if (x+gh->width > v) x = v-gh->width; + v = gdispGGetHeight(gh->display); + if (y+gh->height > v) y = v-gh->height; + if (x < 0) x = 0; + if (y < 0) y = 0; + + // If there has been no move just exit + if (gh->x == x && gh->y == y) + return; + + // Clear the old area and then redraw + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { + // We need to make this window invisible and ensure that has been drawn + gwinSetVisible(gh, FALSE); + _gwinFlushRedraws(REDRAW_WAIT); + + // Do the move + v = gh->x; gh->x = x; x = v; + v = gh->y; gh->y = y; y = v; + + #if GWIN_NEED_CONTAINERS + // Any children need to be moved + if ((gh->flags & GWIN_FLG_CONTAINER)) { + GHandle child; + + // Move to their old relative location. THe WM_Move() will adjust as necessary + for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child)) + WM_Move(gh, child->x-x-((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent), child->y-y-((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent)); + } + #endif + + gwinSetVisible(gh, TRUE); + } else { + v = gh->x; gh->x = x; x = v; + v = gh->y; gh->y = y; y = v; + + #if GWIN_NEED_CONTAINERS + // Any children need to be moved + if ((gh->flags & GWIN_FLG_CONTAINER)) { + GHandle child; + + // Move to their old relative location. THe WM_Move() will adjust as necessary + for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child)) + WM_Move(gh, child->x-x-((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent), child->y-y-((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent)); + } + #endif + } +} + +static void WM_MinMax(GHandle gh, GWindowMinMax minmax) { + (void)gh; (void) minmax; + // We don't support minimising, maximising or restoring +} + +static void WM_Raise(GHandle gh) { + // Take it off the list and then put it back on top + // The order of the list then reflects the z-order. + + gfxQueueASyncRemove(&_GWINList, &gh->wmq); + gfxQueueASyncPut(&_GWINList, &gh->wmq); + + // Redraw the window + _gwinUpdate(gh); +} + +#endif /* GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER */ +/** @} */ diff --git a/src/gwin/gwm.c b/src/gwin/gwm.c deleted file mode 100644 index ba7a132a..00000000 --- a/src/gwin/gwm.c +++ /dev/null @@ -1,847 +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_GWIN && !GWIN_NEED_WINDOWMANAGER - /** - * A really nasty default implementation for the simplest of systems - */ - - - #include "src/gwin/class_gwin.h" - - // Needed if there is no window manager - #define MIN_WIN_WIDTH 1 - #define MIN_WIN_HEIGHT 1 - - static gfxMutex gmutex; - - void _gwmInit(void) { - gfxMutexInit(&gmutex); - } - - void _gwmDeinit(void) { - gfxMutexDestroy(&gmutex); - } - - bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit) { - gh->x = gh->y = gh->width = gh->height = 0; - gwinMove(gh, pInit->x, pInit->y); - gwinResize(gh, pInit->width, pInit->height); - return TRUE; - } - - void _gwinFlushRedraws(GRedrawMethod how) { - (void) how; - - // We are always flushed - } - - - #if GDISP_NEED_CLIP - static void getLock(GHandle gh) { - gfxMutexEnter(&gmutex); - gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); - } - static void exitLock(GHandle gh) { - gdispGUnsetClip(gh->display); - gfxMutexExit(&gmutex); - } - #else - #define getLock(gh) gfxMutexEnter(&gmutex) - #define exitLock(gh) gfxMutexExit(&gmutex) - #endif - - void _gwinUpdate(GHandle gh) { - if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { - if (gh->vmt->Redraw) { - getLock(gh); - gh->vmt->Redraw(gh); - exitLock(gh); - } else if ((gh->flags & GWIN_FLG_BGREDRAW)) { - getLock(gh); - gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); - exitLock(gh); - if (gh->vmt->AfterClear) - gh->vmt->AfterClear(gh); - } - } else if ((gh->flags & GWIN_FLG_BGREDRAW)) { - getLock(gh); - gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor()); - exitLock(gh); - } - gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); - } - - bool_t _gwinDrawStart(GHandle gh) { - if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) - return FALSE; - - getLock(gh); - return TRUE; - } - - void _gwinDrawEnd(GHandle gh) { - (void) gh; - exitLock(gh); - } - - void gwinSetVisible(GHandle gh, bool_t visible) { - if (visible) { - if (!(gh->flags & GWIN_FLG_VISIBLE)) { - gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_BGREDRAW); - _gwinUpdate(gh); - } - } else { - if ((gh->flags & GWIN_FLG_VISIBLE)) { - gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE); - gh->flags |= GWIN_FLG_BGREDRAW; - _gwinUpdate(gh); - } - } - } - - void gwinSetEnabled(GHandle gh, bool_t enabled) { - if (enabled) { - if (!(gh->flags & GWIN_FLG_ENABLED)) { - gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); - _gwinUpdate(gh); - } - } else { - if ((gh->flags & GWIN_FLG_ENABLED)) { - gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); - _gwinUpdate(gh); - } - } - } - - void gwinMove(GHandle gh, coord_t x, coord_t y) { - gh->x = x; gh->y = y; - if (gh->x < 0) gh->x = 0; - if (gh->y < 0) gh->y = 0; - if (gh->x > gdispGGetWidth(gh->display)-MIN_WIN_WIDTH) gh->x = gdispGGetWidth(gh->display)-MIN_WIN_WIDTH; - if (gh->y > gdispGGetHeight(gh->display)-MIN_WIN_HEIGHT) gh->y = gdispGGetHeight(gh->display)-MIN_WIN_HEIGHT; - if (gh->x+gh->width > gdispGGetWidth(gh->display)) gh->width = gdispGGetWidth(gh->display) - gh->x; - if (gh->y+gh->height > gdispGGetHeight(gh->display)) gh->height = gdispGGetHeight(gh->display) - gh->y; - _gwinUpdate(gh); - } - - void gwinResize(GHandle gh, coord_t width, coord_t height) { - gh->width = width; gh->height = height; - if (gh->width < MIN_WIN_WIDTH) { gh->width = MIN_WIN_WIDTH; } - if (gh->height < MIN_WIN_HEIGHT) { gh->height = MIN_WIN_HEIGHT; } - if (gh->x+gh->width > gdispGGetWidth(gh->display)) gh->width = gdispGGetWidth(gh->display) - gh->x; - if (gh->y+gh->height > gdispGGetHeight(gh->display)) gh->height = gdispGGetHeight(gh->display) - gh->y; - _gwinUpdate(gh); - } - - void gwinRedraw(GHandle gh) { - _gwinUpdate(gh); - } -#endif - -#if GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER - -#include "src/gwin/class_gwin.h" - -/*----------------------------------------------- - * Data - *-----------------------------------------------*/ - -// The default window manager -extern const GWindowManager GNullWindowManager; -GWindowManager * _GWINwm; - -static gfxSem gwinsem; -static gfxQueueASync _GWINList; -#if !GWIN_REDRAW_IMMEDIATE - static GTimer RedrawTimer; - static void RedrawTimerFn(void *param); -#endif -static volatile uint8_t RedrawPending; - #define DOREDRAW_INVISIBLES 0x01 - #define DOREDRAW_VISIBLES 0x02 - - -/*----------------------------------------------- - * Window Routines - *-----------------------------------------------*/ - -void _gwmInit(void) -{ - gfxSemInit(&gwinsem, 1, 1); - gfxQueueASyncInit(&_GWINList); - #if !GWIN_REDRAW_IMMEDIATE - gtimerInit(&RedrawTimer); - gtimerStart(&RedrawTimer, RedrawTimerFn, 0, TRUE, TIME_INFINITE); - #endif - _GWINwm = (GWindowManager *)&GNullWindowManager; - _GWINwm->vmt->Init(); -} - -void _gwmDeinit(void) -{ - GHandle gh; - - while((gh = gwinGetNextWindow(0))) - gwinDestroy(gh); - - _GWINwm->vmt->DeInit(); - #if !GWIN_REDRAW_IMMEDIATE - gtimerDeinit(&RedrawTimer); - #endif - gfxQueueASyncDeinit(&_GWINList); - gfxSemDestroy(&gwinsem); -} - -#if GWIN_REDRAW_IMMEDIATE - #define TriggerRedraw(void) _gwinFlushRedraws(REDRAW_NOWAIT); -#else - #define TriggerRedraw() gtimerJab(&RedrawTimer); - - static void RedrawTimerFn(void *param) { - (void) param; - _gwinFlushRedraws(REDRAW_NOWAIT); - } -#endif - -void _gwinFlushRedraws(GRedrawMethod how) { - GHandle gh; - - // Do we really need to do anything? - if (!RedrawPending) - return; - - // Obtain the drawing lock - if (how == REDRAW_WAIT) - gfxSemWait(&gwinsem, TIME_INFINITE); - else if (how == REDRAW_NOWAIT && !gfxSemWait(&gwinsem, TIME_IMMEDIATE)) - // Someone is drawing - They will do the redraw when they are finished - return; - - // Do loss of visibility first - while ((RedrawPending & DOREDRAW_INVISIBLES)) { - RedrawPending &= ~DOREDRAW_INVISIBLES; // Catch new requests - - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) != GWIN_FLG_NEEDREDRAW) - continue; - - // Do the redraw - #if GDISP_NEED_CLIP - gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); - _GWINwm->vmt->Redraw(gh); - gdispGUnsetClip(gh->display); - #else - _GWINwm->vmt->Redraw(gh); - #endif - - // Postpone further redraws - #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP - if (how == REDRAW_NOWAIT) { - RedrawPending |= DOREDRAW_INVISIBLES; - TriggerRedraw(); - goto releaselock; - } - #endif - } - } - - // Do the visible windows next - while ((RedrawPending & DOREDRAW_VISIBLES)) { - RedrawPending &= ~DOREDRAW_VISIBLES; // Catch new requests - - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) - continue; - - // Do the redraw - #if GDISP_NEED_CLIP - gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); - _GWINwm->vmt->Redraw(gh); - gdispGUnsetClip(gh->display); - #else - _GWINwm->vmt->Redraw(gh); - #endif - - // Postpone further redraws (if there are any and the options are set right) - #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP - if (how == REDRAW_NOWAIT) { - while((gh = gwinGetNextWindow(gh))) { - if ((gh->flags & (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_NEEDREDRAW|GWIN_FLG_SYSVISIBLE)) { - RedrawPending |= DOREDRAW_VISIBLES; - TriggerRedraw(); - break; - } - } - goto releaselock; - } - #endif - } - } - - #if !GWIN_REDRAW_IMMEDIATE && !GWIN_REDRAW_SINGLEOP - releaselock: - #endif - - // Release the lock - if (how == REDRAW_WAIT || how == REDRAW_NOWAIT) - gfxSemSignal(&gwinsem); -} - -void _gwinUpdate(GHandle gh) { - // Only redraw if visible - if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) - return; - - // Mark for redraw - gh->flags |= GWIN_FLG_NEEDREDRAW; - RedrawPending |= DOREDRAW_VISIBLES; - - // Asynchronous redraw - TriggerRedraw(); -} - -bool_t _gwinDrawStart(GHandle gh) { - // This test should occur inside the lock. We do this - // here as well as an early out (more efficient). - if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) - return FALSE; - - // Obtain the drawing lock - gfxSemWait(&gwinsem, TIME_INFINITE); - - // Re-test visibility as we may have waited a while - if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) { - _gwinDrawEnd(gh); - return FALSE; - } - - // OK - we are ready to draw. - #if GDISP_NEED_CLIP - gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); - #endif - return TRUE; -} - -void _gwinDrawEnd(GHandle gh) { - // Ensure there is no clip set - #if GDISP_NEED_CLIP - gdispGUnsetClip(gh->display); - #endif - - // Look for something to redraw - _gwinFlushRedraws(REDRAW_INSESSION); - - // Release the lock - gfxSemSignal(&gwinsem); -} - -bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit) { - #if GWIN_NEED_CONTAINERS - // Save the parent - gh->parent = pInit->parent; - - // Ensure the display is consistent with any parents - if (gh->parent && (!(gh->parent->flags & GWIN_FLG_CONTAINER) || gh->display != gh->parent->display)) - return FALSE; - #endif - - // Add to the window manager - if (!_GWINwm->vmt->Add(gh, pInit)) - return FALSE; - - #if GWIN_NEED_CONTAINERS - // Notify the parent it has been added - if (gh->parent && ((gcontainerVMT *)gh->parent->vmt)->NotifyAdd) - ((gcontainerVMT *)gh->parent->vmt)->NotifyAdd(gh->parent, gh); - #endif - - return TRUE; -} - -void gwinSetWindowManager(struct GWindowManager *gwm) { - if (!gwm) - gwm = (GWindowManager *)&GNullWindowManager; - if (_GWINwm != gwm) { - _GWINwm->vmt->DeInit(); - _GWINwm = gwm; - _GWINwm->vmt->Init(); - } -} - -void gwinRedraw(GHandle gh) { - // Only redraw if visible - if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) - return; - - // Mark for redraw - gh->flags |= GWIN_FLG_NEEDREDRAW; - RedrawPending |= DOREDRAW_VISIBLES; - - // Synchronous redraw - _gwinFlushRedraws(REDRAW_WAIT); -} - -#if GWIN_NEED_CONTAINERS - void gwinSetVisible(GHandle gh, bool_t visible) { - if (visible) { - // Mark us as visible - gh->flags |= GWIN_FLG_VISIBLE; - - // Do we want to be added to the display - if (!(gh->flags & GWIN_FLG_SYSVISIBLE) && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE))) { - // Check each window's visibility is consistent with its parents - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if ((gh->flags & (GWIN_FLG_SYSVISIBLE|GWIN_FLG_VISIBLE)) == GWIN_FLG_VISIBLE && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE))) - gh->flags |= (GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); // Fix it and mark for redraw - } - - // Mark for redraw - RedrawPending |= DOREDRAW_VISIBLES; - TriggerRedraw(); - } - } else { - // Mark us as not visible - gh->flags &= ~GWIN_FLG_VISIBLE; - - // Do we need to be removed from the display - if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { - gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); - - // Check each window's visibility is consistent with its parents - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if ((gh->flags & GWIN_FLG_SYSVISIBLE) && (!(gh->flags & GWIN_FLG_VISIBLE) || (gh->parent && !(gh->parent->flags & GWIN_FLG_SYSVISIBLE)))) - gh->flags &= ~GWIN_FLG_SYSVISIBLE; // Fix it - } - - // Mark for redraw - no need to redraw children - RedrawPending |= DOREDRAW_INVISIBLES; - TriggerRedraw(); - } - } - } -#else - void gwinSetVisible(GHandle gh, bool_t visible) { - if (visible) { - if (!(gh->flags & GWIN_FLG_VISIBLE)) { - gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); - RedrawPending |= DOREDRAW_VISIBLES; - TriggerRedraw(); - } - } else { - if ((gh->flags & GWIN_FLG_VISIBLE)) { - gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE); - gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); - RedrawPending |= DOREDRAW_INVISIBLES; - TriggerRedraw(); - } - } - } -#endif - -#if GWIN_NEED_CONTAINERS - // These two sub-functions set/clear system enable recursively. - void gwinSetEnabled(GHandle gh, bool_t enabled) { - if (enabled) { - // Mark us as enabled - gh->flags |= GWIN_FLG_ENABLED; - - // Do we change our real enabled state - if (!(gh->flags & GWIN_FLG_SYSENABLED) && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) { - // Check each window's enabled state is consistent with its parents - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if ((gh->flags & (GWIN_FLG_SYSENABLED|GWIN_FLG_ENABLED)) == GWIN_FLG_ENABLED && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) { - gh->flags |= GWIN_FLG_SYSENABLED; // Fix it - _gwinUpdate(gh); - } - } - } - } else { - gh->flags &= ~GWIN_FLG_ENABLED; - - // Do we need to change our real enabled state - if ((gh->flags & GWIN_FLG_SYSENABLED)) { - // Check each window's visibility is consistent with its parents - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if ((gh->flags & GWIN_FLG_SYSENABLED) && (!(gh->flags & GWIN_FLG_ENABLED) || (gh->parent && !(gh->parent->flags & GWIN_FLG_SYSENABLED)))) { - gh->flags &= ~GWIN_FLG_SYSENABLED; // Fix it - _gwinUpdate(gh); - } - } - } - } - } -#else - void gwinSetEnabled(GHandle gh, bool_t enabled) { - if (enabled) { - if (!(gh->flags & GWIN_FLG_ENABLED)) { - gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); - _gwinUpdate(gh); - } - } else { - if ((gh->flags & GWIN_FLG_ENABLED)) { - gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); - _gwinUpdate(gh); - } - } - } -#endif - -void gwinMove(GHandle gh, coord_t x, coord_t y) { - _GWINwm->vmt->Move(gh, x, y); -} - -void gwinResize(GHandle gh, coord_t width, coord_t height) { - _GWINwm->vmt->Size(gh, width, height); -} - -void gwinSetMinMax(GHandle gh, GWindowMinMax minmax) { - _GWINwm->vmt->MinMax(gh, minmax); -} - -void gwinRaise(GHandle gh) { - _GWINwm->vmt->Raise(gh); -} - -GWindowMinMax gwinGetMinMax(GHandle gh) { - if (gh->flags & GWIN_FLG_MINIMIZED) - return GWIN_MINIMIZE; - if (gh->flags & GWIN_FLG_MAXIMIZED) - return GWIN_MAXIMIZE; - return GWIN_NORMAL; -} - -void gwinRedrawDisplay(GDisplay *g, bool_t preserve) { - GHandle gh; - - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - - // Skip if it is for a different display - if (g && gh->display != g) - continue; - - #if GWIN_NEED_CONTAINERS - // Skip if it is not a top level window (parents internally take care of their children) - if (gh->parent) - continue; - #endif - - // Only visible windows are to be redrawn - if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) - continue; - - if (!preserve) - gh->flags |= GWIN_FLG_BGREDRAW; - - _gwinUpdate(gh); - } -} - -GHandle gwinGetNextWindow(GHandle gh) { - return gh ? (GHandle)gfxQueueASyncNext(&gh->wmq) : (GHandle)gfxQueueASyncPeek(&_GWINList); -} - -/*----------------------------------------------- - * "Null" Window Manager Routines - *-----------------------------------------------*/ - -// This is a parent reveal operation -#define GWIN_FLG_PARENTREVEAL (GWIN_FIRST_WM_FLAG << 0) - -// Minimum dimensions -#define MIN_WIN_WIDTH 3 -#define MIN_WIN_HEIGHT 3 - - -static void WM_Init(void); -static void WM_DeInit(void); -static bool_t WM_Add(GHandle gh, const GWindowInit *pInit); -static void WM_Delete(GHandle gh); -static void WM_Redraw(GHandle gh); -static void WM_Size(GHandle gh, coord_t w, coord_t h); -static void WM_Move(GHandle gh, coord_t x, coord_t y); -static void WM_Raise(GHandle gh); -static void WM_MinMax(GHandle gh, GWindowMinMax minmax); - -static const gwmVMT GNullWindowManagerVMT = { - WM_Init, - WM_DeInit, - WM_Add, - WM_Delete, - WM_Redraw, - WM_Size, - WM_Move, - WM_Raise, - WM_MinMax, -}; - -const GWindowManager GNullWindowManager = { - &GNullWindowManagerVMT, -}; - -static void WM_Init(void) { - // We don't need to do anything here. - // A full window manager would move the windows around, add borders etc - - // clear the screen - // cycle through the windows already defined displaying them - // or cut all the window areas out of the screen and clear the remainder -} - -static void WM_DeInit(void) { - // We don't need to do anything here. - // A full window manager would remove any borders etc -} - -static bool_t WM_Add(GHandle gh, const GWindowInit *pInit) { - // Note the window will not currently be marked as visible - - // Put it on the end of the queue - gfxQueueASyncPut(&_GWINList, &gh->wmq); - - // Make sure the size/position is valid - prefer position over size. - gh->width = MIN_WIN_WIDTH; gh->height = MIN_WIN_HEIGHT; - gh->x = gh->y = 0; - WM_Move(gh, pInit->x, pInit->y); - WM_Size(gh, pInit->width, pInit->height); - return TRUE; -} - -static void WM_Delete(GHandle gh) { - // Remove it from the window list - gfxQueueASyncRemove(&_GWINList, &gh->wmq); -} - -static void WM_Redraw(GHandle gh) { - #if GWIN_NEED_CONTAINERS - redo_redraw: - #endif - if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { - if (gh->vmt->Redraw) - gh->vmt->Redraw(gh); - else if ((gh->flags & GWIN_FLG_BGREDRAW)) { - // We can't redraw but we want full coverage so just clear the area - gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); - - // Only do an after clear if this is not a parent reveal - if (!(gh->flags & GWIN_FLG_PARENTREVEAL) && gh->vmt->AfterClear) - gh->vmt->AfterClear(gh); - } - - // Redraw is done - gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL); - - #if GWIN_NEED_CONTAINERS - // If this is container but not a parent reveal, mark any visible children for redraw - // We redraw our children here as we have overwritten them in redrawing the parent - // as GDISP/GWIN doesn't yet support complex clipping regions. - if ((gh->flags & (GWIN_FLG_CONTAINER|GWIN_FLG_PARENTREVEAL)) == GWIN_FLG_CONTAINER) { - for(gh = gwinGetFirstChild(gh); gh; gh = gwinGetSibling(gh)) - _gwinUpdate(gh); - } - #endif - } else { - if ((gh->flags & GWIN_FLG_BGREDRAW)) { - GHandle gx; - - #if GWIN_NEED_CONTAINERS - if (gh->parent) { - // Child redraw is done - gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL); - - // Get the parent to redraw the area - gh = gh->parent; - gh->flags |= (GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL); - goto redo_redraw; - } - #endif - - // Clear the area to the background color - gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor()); - - // Now loop over all windows looking for overlaps. Redraw them if they overlap the newly exposed area. - for(gx = gwinGetNextWindow(0); gx; gx = gwinGetNextWindow(gx)) { - if ((gx->flags & GWIN_FLG_SYSVISIBLE) - && gx->display == gh->display - && gx->x < gh->x+gh->width && gx->y < gh->y+gh->height && gx->x+gx->width >= gh->x && gx->y+gx->height >= gh->y) { - if (gx->vmt->Redraw) - gx->vmt->Redraw(gx); - else - // We can't redraw this window but we want full coverage so just clear the area - gdispGFillArea(gx->display, gx->x, gx->y, gx->width, gx->height, gx->bgcolor); - } - } - } - - // Redraw is done - gh->flags &= ~(GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW|GWIN_FLG_PARENTREVEAL); - } -} - -static void WM_Size(GHandle gh, coord_t w, coord_t h) { - coord_t v; - - #if GWIN_NEED_CONTAINERS - if (gh->parent) { - // Clip to the container - v = gh->parent->x + gh->parent->width - ((const gcontainerVMT *)gh->parent->vmt)->RightBorder(gh->parent); - if (gh->x+w > v) w = v - gh->x; - v = gh->parent->y + gh->parent->height - ((const gcontainerVMT *)gh->parent->vmt)->BottomBorder(gh->parent); - if (gh->y+h > v) h = v - gh->y; - } - #endif - - // Clip to the screen - v = gdispGGetWidth(gh->display); - if (gh->x+w > v) w = v - gh->x; - v = gdispGGetHeight(gh->display); - if (gh->y+h > v) h = v - gh->y; - - // Give it a minimum size - if (w < MIN_WIN_WIDTH) w = MIN_WIN_WIDTH; - if (h < MIN_WIN_HEIGHT) h = MIN_WIN_HEIGHT; - - // If there has been no resize just exit - if (gh->width == w && gh->height == h) - return; - - // Set the new size and redraw - if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { - if (w >= gh->width && h >= gh->height) { - - // The new size is larger - just redraw - gh->width = w; gh->height = h; - _gwinUpdate(gh); - - } else { - // We need to make this window invisible and ensure that has been drawn - gwinSetVisible(gh, FALSE); - _gwinFlushRedraws(REDRAW_WAIT); - - // Resize - gh->width = w; gh->height = h; - - #if GWIN_NEED_CONTAINERS - // Any children outside the new area need to be moved - if ((gh->flags & GWIN_FLG_CONTAINER)) { - GHandle child; - - // Move to their old relative location. THe WM_Move() will adjust as necessary - for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child)) - WM_Move(gh, child->x-gh->x-((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent), child->y-gh->y-((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent)); - } - #endif - - // Mark it visible again in its new location - gwinSetVisible(gh, TRUE); - } - } else { - gh->width = w; gh->height = h; - - #if GWIN_NEED_CONTAINERS - // Any children outside the new area need to be moved - if ((gh->flags & GWIN_FLG_CONTAINER)) { - GHandle child; - - // Move to their old relative location. THe WM_Move() will adjust as necessary - for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child)) - WM_Move(gh, child->x-gh->x-((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent), child->y-gh->y-((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent)); - } - #endif - } -} - -static void WM_Move(GHandle gh, coord_t x, coord_t y) { - coord_t v; - - #if GWIN_NEED_CONTAINERS - if (gh->parent) { - // Clip to the parent size - v = gh->parent->width - ((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent) - ((const gcontainerVMT *)gh->parent->vmt)->RightBorder(gh->parent); - if (x+gh->width > v) x = v-gh->width; - v = gh->parent->height - ((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent) - ((const gcontainerVMT *)gh->parent->vmt)->BottomBorder(gh->parent); - if (y+gh->height > v) y = v-gh->height; - if (x < 0) x = 0; - if (y < 0) y = 0; - - // Convert to absolute position - x += gh->parent->x + ((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent); - y += gh->parent->y + ((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent); - } - #endif - - // Clip to the screen - v = gdispGGetWidth(gh->display); - if (x+gh->width > v) x = v-gh->width; - v = gdispGGetHeight(gh->display); - if (y+gh->height > v) y = v-gh->height; - if (x < 0) x = 0; - if (y < 0) y = 0; - - // If there has been no move just exit - if (gh->x == x && gh->y == y) - return; - - // Clear the old area and then redraw - if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { - // We need to make this window invisible and ensure that has been drawn - gwinSetVisible(gh, FALSE); - _gwinFlushRedraws(REDRAW_WAIT); - - // Do the move - v = gh->x; gh->x = x; x = v; - v = gh->y; gh->y = y; y = v; - - #if GWIN_NEED_CONTAINERS - // Any children need to be moved - if ((gh->flags & GWIN_FLG_CONTAINER)) { - GHandle child; - - // Move to their old relative location. THe WM_Move() will adjust as necessary - for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child)) - WM_Move(gh, child->x-x-((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent), child->y-y-((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent)); - } - #endif - - gwinSetVisible(gh, TRUE); - } else { - v = gh->x; gh->x = x; x = v; - v = gh->y; gh->y = y; y = v; - - #if GWIN_NEED_CONTAINERS - // Any children need to be moved - if ((gh->flags & GWIN_FLG_CONTAINER)) { - GHandle child; - - // Move to their old relative location. THe WM_Move() will adjust as necessary - for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child)) - WM_Move(gh, child->x-x-((const gcontainerVMT *)gh->parent->vmt)->LeftBorder(gh->parent), child->y-y-((const gcontainerVMT *)gh->parent->vmt)->TopBorder(gh->parent)); - } - #endif - } -} - -static void WM_MinMax(GHandle gh, GWindowMinMax minmax) { - (void)gh; (void) minmax; - // We don't support minimising, maximising or restoring -} - -static void WM_Raise(GHandle gh) { - // Take it off the list and then put it back on top - // The order of the list then reflects the z-order. - - gfxQueueASyncRemove(&_GWINList, &gh->wmq); - gfxQueueASyncPut(&_GWINList, &gh->wmq); - - // Redraw the window - _gwinUpdate(gh); -} - -#endif /* GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER */ -/** @} */ diff --git a/src/gwin/label.c b/src/gwin/label.c deleted file mode 100644 index 4faba95c..00000000 --- a/src/gwin/label.c +++ /dev/null @@ -1,168 +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/label.c - * @brief GWIN label widget header file - */ - -#include "gfx.h" - -#if GFX_USE_GWIN && GWIN_NEED_LABEL - -#include "src/gwin/class_gwin.h" - -// macros to assist in data type conversions -#define gh2obj ((GLabelObject *)gh) -#define gw2obj ((GLabelObject *)gw) - -// flags for the GLabelObject -#define GLABEL_FLG_WAUTO (GWIN_FIRST_CONTROL_FLAG << 0) -#define GLABEL_FLG_HAUTO (GWIN_FIRST_CONTROL_FLAG << 1) -#define GLABEL_FLG_BORDER (GWIN_FIRST_CONTROL_FLAG << 2) - -// simple: single line with no wrapping -static coord_t getwidth(const char *text, font_t font, coord_t maxwidth) { - (void) maxwidth; - - return gdispGetStringWidth(text, font)+2; // Allow one pixel of padding on each side -} - -// simple: single line with no wrapping -static coord_t getheight(const char *text, font_t font, coord_t maxwidth) { - (void) text; - (void) maxwidth; - - return gdispGetFontMetric(font, fontHeight); -} - -static void gwinLabelDefaultDraw(GWidgetObject *gw, void *param); - -static const gwidgetVMT labelVMT = { - { - "Label", // The class name - sizeof(GLabelObject), // The object size - _gwidgetDestroy, // The destroy routine - _gwidgetRedraw, // The redraw routine - 0, // The after-clear routine - }, - gwinLabelDefaultDraw, // default drawing routine - #if GINPUT_NEED_MOUSE - { - 0, // Process mose down events (NOT USED) - 0, // Process mouse up events (NOT USED) - 0, // Process mouse move events (NOT USED) - }, - #endif - #if GINPUT_NEED_TOGGLE - { - 0, // No toggle role - 0, // Assign Toggles (NOT USED) - 0, // Get Toggles (NOT USED) - 0, // Process toggle off event (NOT USED) - 0, // Process toggle on event (NOT USED) - }, - #endif - #if GINPUT_NEED_DIAL - { - 0, // No dial roles - 0, // Assign Dials (NOT USED) - 0, // Get Dials (NOT USED) - 0, // Procees dial move events (NOT USED) - }, - #endif -}; - -GHandle gwinGLabelCreate(GDisplay *g, GLabelObject *widget, GWidgetInit *pInit) { - uint16_t flags = 0; - - // auto assign width - if (pInit->g.width <= 0) { - - flags |= GLABEL_FLG_WAUTO; - pInit->g.width = getwidth(pInit->text, gwinGetDefaultFont(), gdispGGetWidth(g) - pInit->g.x); - } - - // auto assign height - if (pInit->g.height <= 0) { - flags |= GLABEL_FLG_HAUTO; - pInit->g.height = getheight(pInit->text, gwinGetDefaultFont(), gdispGGetWidth(g) - pInit->g.x); - } - - if (!(widget = (GLabelObject *)_gwidgetCreate(g, &widget->w, pInit, &labelVMT))) - return 0; - - #if GWIN_LABEL_ATTRIBUTE - widget->tab = 0; - widget->attr = 0; - #endif - - widget->w.g.flags |= flags; - gwinSetVisible(&widget->w.g, pInit->g.show); - - return (GHandle)widget; -} - -void gwinLabelSetBorder(GHandle gh, bool_t border) { - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&labelVMT) - return; - - if (border) - gh2obj->w.g.flags |= GLABEL_FLG_BORDER; - else - gh2obj->w.g.flags &=~ GLABEL_FLG_BORDER; -} - -#if GWIN_LABEL_ATTRIBUTE - void gwinLabelSetAttribute(GHandle gh, coord_t tab, const char* attr) { - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&labelVMT) - return; - - gh2obj->tab = tab; - gh2obj->attr = attr; - - gwinRedraw(gh); - } -#endif // GWIN_LABEL_ATTRIBUTE - -static void gwinLabelDefaultDraw(GWidgetObject *gw, void *param) { - coord_t w, h; - color_t c; - (void) param; - - // is it a valid handle? - if (gw->g.vmt != (gwinVMT *)&labelVMT) - return; - - w = (gw->g.flags & GLABEL_FLG_WAUTO) ? getwidth(gw->text, gw->g.font, gdispGGetWidth(gw->g.display) - gw->g.x) : gw->g.width; - h = (gw->g.flags & GLABEL_FLG_HAUTO) ? getheight(gw->text, gw->g.font, gdispGGetWidth(gw->g.display) - gw->g.x) : gw->g.height; - c = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text; - - if (gw->g.width != w || gw->g.height != h) { - gwinResize(&gw->g, w, h); - - return; - } - - #if GWIN_LABEL_ATTRIBUTE - if (gw2obj->attr) { - gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw2obj->tab, h, gw2obj->attr, gw->g.font, c, gw->pstyle->background, justifyLeft); - gdispGFillStringBox(gw->g.display, gw->g.x + gw2obj->tab, gw->g.y, w-gw2obj->tab, h, gw->text, gw->g.font, c, gw->pstyle->background, justifyLeft); - } else - gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, w, h, gw->text, gw->g.font, c, gw->pstyle->background, justifyLeft); - #else - gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, w, h, gw->text, gw->g.font, c, gw->pstyle->background, justifyLeft); - #endif - - // render the border (if any) - if (gw->g.flags & GLABEL_FLG_BORDER) - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, w, h, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); -} - -#endif // GFX_USE_GWIN && GFX_NEED_LABEL diff --git a/src/gwin/label.h b/src/gwin/label.h deleted file mode 100644 index e0b32240..00000000 --- a/src/gwin/label.h +++ /dev/null @@ -1,104 +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/label.h - * @brief GWIN label widget header file - * - * @defgroup Label Label - * @ingroup Widgets - * - * @details GWIN allos it to create an label widget. The widget - * takes no user input. - * - * @pre GFX_USE_GDISP must be set to TRUE in your gfxconf.h - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GDISP_NEED_TEXT must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_LABEL must be set to TRUE in your gfxconf.h - * @pre The fonts you want to use must be enabled in your gfxconf.h - * - * @{ - */ - -#ifndef _GWIN_LABEL_H -#define _GWIN_LABEL_H - -// This file is included within "gwin/gwin.h" - -// An label window -typedef struct GLabelObject { - GWidgetObject w; - - #if GWIN_LABEL_ATTRIBUTE - coord_t tab; - const char* attr; - #endif -} GLabelObject; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create a label widget. - * @details A label widget is a simple window which has a static text. - * - * @param[in] g The GDisplay to display this window on - * @param[in] widget The label structure to initialise. If this is NULL, the structure is dynamically allocated. - * @param[in] pInit The initialisation parameters to use. - * - * @return NULL if there is no resultat drawing area, otherwise the widget handle. - * - * @api - */ -GHandle gwinGLabelCreate(GDisplay *g, GLabelObject *widget, GWidgetInit *pInit); -#define gwinLabelCreate(w, pInit) gwinGLabelCreate(GDISP, w, pInit) - -/** - * @brief Border settings for the default rendering routine - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] border Shall a border be rendered? - * - * @api - */ -void gwinLabelSetBorder(GHandle gh, bool_t border); - -#if GWIN_LABEL_ATTRIBUTE || defined(__DOXYGEN__) - /** - * @brief Add an text attribute in front of the normal label text - * @details Often you want to display a text like this: - * Current IP: 192.168.1.42 - * In that case, the actual IP will be variable, the text in front of it - * always remains the same. The static text is called the attribute and can be - * set using this function. - * Furthermore, the tab can be set in order to vertically align multiple labels. - * Please check out the website for further explanation, illustraions and usage - * examples. - * - * @note The attribute text is not copied into private memory and so it - * must be a constant string, not one allocated in a stack buffer. - * @note Use of this construct is discouraged. The appropriate way is to - * create two labels - one for the static text and one for the - * dynamic text. - * - * @param[in] gh The widget handle (must be a label handle) - * @param[in] tab The distance of the label text from the left widget edge - * @param[in] attr The attribute to be displayed - * - * @api - */ - void gwinLabelSetAttribute(GHandle gh, coord_t tab, const char* attr); -#endif - -#ifdef __cplusplus -} -#endif - -#endif // _GWIN_LABEL_H -/** @} */ - diff --git a/src/gwin/list.c b/src/gwin/list.c deleted file mode 100644 index 10926238..00000000 --- a/src/gwin/list.c +++ /dev/null @@ -1,696 +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/list.c - * @brief GWIN list widget header file - */ - -#include "gfx.h" - -#if GFX_USE_GWIN && GWIN_NEED_LIST - -#include "src/gwin/class_gwin.h" -#include -#include - -// user for the default drawing routine -#define SCROLLWIDTH 16 // the border from the scroll buttons to the frame -#define ARROW 10 // arrow side length -#define HORIZONTAL_PADDING 5 // extra horizontal padding for text -#define VERTICAL_PADDING 2 // extra vertical padding for text - -// Macro's to assist in data type conversions -#define gh2obj ((GListObject *)gh) -#define gw2obj ((GListObject *)gw) -#define qi2li ((ListItem *)qi) -#define qix2li ((ListItem *)qix) -#define ple ((GEventGWinList *)pe) - -// Flags for the GListObject -#define GLIST_FLG_MULTISELECT (GWIN_FIRST_CONTROL_FLAG << 0) -#define GLIST_FLG_HASIMAGES (GWIN_FIRST_CONTROL_FLAG << 1) -#define GLIST_FLG_SCROLLALWAYS (GWIN_FIRST_CONTROL_FLAG << 2) -#define GLIST_FLG_SCROLLSMOOTH (GWIN_FIRST_CONTROL_FLAG << 3) -#define GLIST_FLG_ENABLERENDER (GWIN_FIRST_CONTROL_FLAG << 4) - -// Flags on a ListItem. -#define GLIST_FLG_SELECTED 0x0001 - -typedef struct ListItem { - gfxQueueASyncItem q_item; // This must be the first member in the struct - - uint16_t flags; - uint16_t param; // A parameter the user can specify himself - const char* text; - #if GWIN_NEED_LIST_IMAGES - gdispImage* pimg; - #endif -} ListItem; - -static void sendListEvent(GWidgetObject *gw, int item) { - GSourceListener* psl; - GEvent* pe; - - // Trigger a GWIN list event - psl = 0; - - while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { - if (!(pe = geventGetEventBuffer(psl))) - continue; - - ple->type = GEVENT_GWIN_LIST; - ple->gwin = (GHandle)gw; - ple->item = item; - #if GWIN_WIDGET_TAGS - ple->tag = gw->tag; - #endif - - geventSendEvent(psl); - } -} - -static void gwinListDefaultDraw(GWidgetObject* gw, void* param); - -#if GINPUT_NEED_MOUSE - static void MouseSelect(GWidgetObject* gw, coord_t x, coord_t y) { - const gfxQueueASyncItem* qi; - int item, i; - coord_t iheight; - (void) x; - - iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING; - - // Handle click over the list area - item = (gw2obj->top + y) / iheight; - - if (item < 0 || item >= gw2obj->cnt) - return; - - for(qi = gfxQueueASyncPeek(&gw2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { - if ((gw->g.flags & GLIST_FLG_MULTISELECT)) { - if (item == i) { - qi2li->flags ^= GLIST_FLG_SELECTED; - break; - } - } else { - if (item == i) - qi2li->flags |= GLIST_FLG_SELECTED; - else - qi2li->flags &=~ GLIST_FLG_SELECTED; - } - } - - _gwinUpdate(&gw->g); - sendListEvent(gw, item); - - } - - // a mouse down has occurred over the list area - static void MouseDown(GWidgetObject* gw, coord_t x, coord_t y) { - coord_t iheight, pgsz; - - // Save our mouse start position - gw2obj->start_mouse_x = x; - gw2obj->start_mouse_y = y; - gw2obj->last_mouse_y = y; - - // For smooth scrolling, scrolling is done in the MouseMove and selection is done on MouseUp - if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH) - return; - - // Some initial stuff - iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING; - pgsz = gw->g.height-2; - - // Handle click over the scroll bar - if (x >= gw->g.width-(SCROLLWIDTH+2) && (gw2obj->cnt > pgsz/iheight || (gw->g.flags & GLIST_FLG_SCROLLALWAYS))) { - if (y < 2*ARROW) { - if (gw2obj->top > 0) { - gw2obj->top -= iheight; - if (gw2obj->top < 0) - gw2obj->top = 0; - _gwinUpdate(&gw->g); - } - } else if (y >= gw->g.height - 2*ARROW) { - if (gw2obj->top < gw2obj->cnt * iheight - pgsz) { - gw2obj->top += iheight; - if (gw2obj->top > gw2obj->cnt * iheight - pgsz) - gw2obj->top = gw2obj->cnt * iheight - pgsz; - _gwinUpdate(&gw->g); - } - } else if (y < gw->g.height/2) { - if (gw2obj->top > 0) { - if (gw2obj->top > pgsz) - gw2obj->top -= pgsz; - else - gw2obj->top = 0; - _gwinUpdate(&gw->g); - } - } else { - if (gw2obj->top < gw2obj->cnt * iheight - pgsz) { - if (gw2obj->top < gw2obj->cnt * iheight - 2*pgsz) - gw2obj->top += pgsz; - else - gw2obj->top = gw2obj->cnt * iheight - pgsz; - _gwinUpdate(&gw->g); - } - } - return; - } - - MouseSelect(gw, x, y); - } - - static void MouseUp(GWidgetObject* gw, coord_t x, coord_t y) { - // Only act when we are a smooth scrolling list - if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH)) - return; - - // Only allow selection when we did not scroll - if (abs(gw2obj->start_mouse_x - x) > 4 || abs(gw2obj->start_mouse_y - y) > 4) - return; - - MouseSelect(gw, x, y); - } - - static void MouseMove(GWidgetObject* gw, coord_t x, coord_t y) { - int iheight, oldtop; - (void) x; - - if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH)) return; - - if (gw2obj->last_mouse_y != y) { - oldtop = gw2obj->top; - iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING; - - gw2obj->top -= y - gw2obj->last_mouse_y; - if (gw2obj->top >= gw2obj->cnt * iheight - (gw->g.height-2)) - gw2obj->top = gw2obj->cnt * iheight - (gw->g.height-2) - 1; - if (gw2obj->top < 0) - gw2obj->top = 0; - gw2obj->last_mouse_y = y; - if (oldtop != gw2obj->top) - _gwinUpdate(&gw->g); - } - } -#endif - -#if GINPUT_NEED_TOGGLE - // a toggle-on has occurred - static void ToggleOn(GWidgetObject *gw, uint16_t role) { - const gfxQueueASyncItem * qi; - const gfxQueueASyncItem * qix; - int i; - - switch (role) { - // select down - case 0: - for (i = 0, qi = gfxQueueASyncPeek(&gw2obj->list_head); qi; qi = gfxQueueASyncNext(qi), i++) { - if ((qi2li->flags & GLIST_FLG_SELECTED)) { - qix = gfxQueueASyncNext(qi); - if (qix) { - qi2li->flags &=~ GLIST_FLG_SELECTED; - qix2li->flags |= GLIST_FLG_SELECTED; - _gwinUpdate(&gw->g); - } - break; - } - } - break; - - // select up - case 1: - qi = gfxQueueASyncPeek(&gw2obj->list_head); - qix = 0; - - for (i = 0; qi; qix = qi, qi = gfxQueueASyncNext(qi), i++) { - if ((qi2li->flags & GLIST_FLG_SELECTED)) - if (qix) { - qi2li->flags &=~ GLIST_FLG_SELECTED; - qix2li->flags |= GLIST_FLG_SELECTED; - _gwinUpdate(&gw->g); - } - break; - } - } - break; - } - } - - static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { - if (role) - gw2obj->t_up = instance; - else - gw2obj->t_dn = instance; - } - - static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { - return role ? gw2obj->t_up : gw2obj->t_dn; - } -#endif - -static void _destroy(GHandle gh) { - const gfxQueueASyncItem* qi; - - while((qi = gfxQueueASyncGet(&gh2obj->list_head))) - gfxFree((void *)qi); - - _gwidgetDestroy(gh); -} - -static const gwidgetVMT listVMT = { - { - "List", // The class name - sizeof(GListObject), // The object size - _destroy, // The destroy routine - _gwidgetRedraw, // The redraw routine - 0, // The after-clear routine - }, - gwinListDefaultDraw, // default drawing routine - #if GINPUT_NEED_MOUSE - { - MouseDown, - MouseUp, - MouseMove, - }, - #endif - #if GINPUT_NEED_TOGGLE - { - 2, // two toggle roles - ToggleAssign, // Assign toggles - ToggleGet, // get toggles - 0, - ToggleOn, // process toggle on event - }, - #endif - #if GINPUT_NEED_DIAL - { - 0, - 0, - 0, - 0, - }, - #endif -}; - -GHandle gwinGListCreate(GDisplay *g, GListObject* gobj, GWidgetInit* pInit, bool_t multiselect) { - if (!(gobj = (GListObject *)_gwidgetCreate(g, &gobj->w, pInit, &listVMT))) - return 0; - - // initialize the item queue - gfxQueueASyncInit(&gobj->list_head); - gobj->cnt = 0; - gobj->top = 0; - if (multiselect) - gobj->w.g.flags |= GLIST_FLG_MULTISELECT; - gobj->w.g.flags |= GLIST_FLG_SCROLLALWAYS; - gobj->w.g.flags |= GLIST_FLG_ENABLERENDER; - - gwinSetVisible(&gobj->w.g, pInit->g.show); - - return (GHandle)gobj; -} - -void gwinListEnableRender(GHandle gh, bool_t ena) { - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return; - - if (ena) { - gh->flags |= GLIST_FLG_ENABLERENDER; - gwinRedraw(gh); - } else { - gh->flags &=~ GLIST_FLG_ENABLERENDER; - } -} - -void gwinListSetScroll(GHandle gh, scroll_t flag) { - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return; - - ((GListObject*)gh)->w.g.flags &=~(GLIST_FLG_SCROLLSMOOTH | GLIST_FLG_SCROLLALWAYS); - switch (flag) { - case scrollAlways: - ((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLALWAYS; - break; - - case scrollAuto: - break; - - case scrollSmooth: - ((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLSMOOTH; - break; - } -} - -int gwinListAddItem(GHandle gh, const char* item_name, bool_t useAlloc) { - ListItem *newItem; - - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return -1; - - if (useAlloc) { - size_t len = strlen(item_name)+1; - if (!(newItem = gfxAlloc(sizeof(ListItem) + len))) - return -1; - - memcpy((char *)(newItem+1), item_name, len); - item_name = (const char *)(newItem+1); - } else { - if (!(newItem = gfxAlloc(sizeof(ListItem)))) - return -1; - } - - // the item is not selected when added - newItem->flags = 0; - newItem->param = 0; - newItem->text = item_name; - #if GWIN_NEED_LIST_IMAGES - newItem->pimg = 0; - #endif - - // select the item if it's the first in the list - if (gh2obj->cnt == 0 && !(gh->flags & GLIST_FLG_MULTISELECT)) - newItem->flags |= GLIST_FLG_SELECTED; - - // add the new item to the list - gfxQueueASyncPut(&gh2obj->list_head, &newItem->q_item); - - // increment the total amount of entries in the list widget - gh2obj->cnt++; - - _gwinUpdate(gh); - - // return the position in the list (-1 because we start with index 0) - return gh2obj->cnt-1; -} - -const char* gwinListItemGetText(GHandle gh, int item) { - const gfxQueueASyncItem* qi; - int i; - - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return 0; - - // watch out for an invalid item - if (item < 0 || item >= gh2obj->cnt) - return 0; - - for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { - if (i == item) - return qi2li->text; - } - return 0; -} - -int gwinListFindText(GHandle gh, const char* text) { - const gfxQueueASyncItem* qi; - int i; - - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return -1; - - // watch out for NULL pointers - if (!text) - return -1; - - for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { - if (strcmp(((ListItem *)qi)->text, text) == 0) - return i; - } - - return -1; -} - -int gwinListGetSelected(GHandle gh) { - const gfxQueueASyncItem * qi; - int i; - - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return -1; - - // Multi-select always returns -1. Use gwinListItemIsSelected() instead - if ((gh->flags & GLIST_FLG_MULTISELECT)) - return -1; - - for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { - if (qi2li->flags & GLIST_FLG_SELECTED) - return i; - } - - return -1; -} - -void gwinListItemSetParam(GHandle gh, int item, uint16_t param) { - const gfxQueueASyncItem * qi; - int i; - - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return; - - // watch out for an invalid item - if (item < 0 || item > (gh2obj->cnt) - 1) - return; - - for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { - if (i == item) { - qi2li->param = param; - break; - } - } -} - -void gwinListDeleteAll(GHandle gh) { - gfxQueueASyncItem* qi; - - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return; - - while((qi = gfxQueueASyncGet(&gh2obj->list_head))) - gfxFree(qi); - - gh->flags &= ~GLIST_FLG_HASIMAGES; - gh2obj->cnt = 0; - gh2obj->top = 0; - _gwinUpdate(gh); -} - -void gwinListItemDelete(GHandle gh, int item) { - const gfxQueueASyncItem * qi; - int i; - - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return; - - // watch out for an invalid item - if (item < 0 || item >= gh2obj->cnt) - return; - - for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { - if (i == item) { - gfxQueueASyncRemove(&gh2obj->list_head, (gfxQueueASyncItem*)qi); - gfxFree((void *)qi); - if (gh2obj->top >= item && gh2obj->top) - gh2obj->top--; - _gwinUpdate(gh); - break; - } - } -} - -uint16_t gwinListItemGetParam(GHandle gh, int item) { - const gfxQueueASyncItem * qi; - int i; - - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return 0; - - // watch out for an invalid item - if (item < 0 || item > (gh2obj->cnt) - 1) - return 0; - - for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { - if (i == item) - return qi2li->param; - } - return 0; -} - -bool_t gwinListItemIsSelected(GHandle gh, int item) { - const gfxQueueASyncItem * qi; - int i; - - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return FALSE; - - // watch out for an invalid item - if (item < 0 || item > (gh2obj->cnt) - 1) - return FALSE; - - for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { - if (i == item) - return (qi2li->flags & GLIST_FLG_SELECTED) ? TRUE : FALSE; - } - return FALSE; -} - -int gwinListItemCount(GHandle gh) { - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return 0; - - return gh2obj->cnt; -} - -const char* gwinListGetSelectedText(GHandle gh) { - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return 0; - - // return NULL if nothing is selected (or multi-select) - if (gwinListGetSelected(gh) < 0) - return 0; - - return gwinListItemGetText(gh, gwinListGetSelected(gh)); -} - -#if GWIN_NEED_LIST_IMAGES - void gwinListItemSetImage(GHandle gh, int item, gdispImage *pimg) { - const gfxQueueASyncItem * qi; - int i; - - // is it a valid handle? - if (gh->vmt != (gwinVMT *)&listVMT) - return; - - // watch out for an invalid item - if (item < 0 || item > (gh2obj->cnt) - 1) - return; - - for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) { - if (i == item) { - qi2li->pimg = pimg; - if (pimg) - gh->flags |= GLIST_FLG_HASIMAGES; - break; - } - } - } -#endif - -static void gwinListDefaultDraw(GWidgetObject* gw, void* param) { - (void)param; - - #if GDISP_NEED_CONVEX_POLYGON - static const point upArrow[] = { {0, ARROW}, {ARROW, ARROW}, {ARROW/2, 0} }; - static const point downArrow[] = { {0, 0}, {ARROW, 0}, {ARROW/2, ARROW} }; - #endif - - const gfxQueueASyncItem* qi; - int i; - coord_t x, y, iheight, iwidth; - color_t fill; - const GColorSet * ps; - #if GWIN_NEED_LIST_IMAGES - coord_t sy; - #endif - - // is it a valid handle? - if (gw->g.vmt != (gwinVMT *)&listVMT) - return; - - // don't render if render has been disabled - if (!(gw->g.flags & GLIST_FLG_ENABLERENDER)) - return; - - ps = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled; - iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING; - x = 1; - - // the scroll area - if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH) { - iwidth = gw->g.width - 2 - 4; - if (gw2obj->cnt > 0) { - int max_scroll_value = gw2obj->cnt * iheight - gw->g.height-2; - if (max_scroll_value > 0) { - int bar_height = (gw->g.height-2) * (gw->g.height-2) / (gw2obj->cnt * iheight); - gdispGFillArea(gw->g.display, gw->g.x + gw->g.width-4, gw->g.y + 1, 2, gw->g.height-1, gw->pstyle->background); - gdispGFillArea(gw->g.display, gw->g.x + gw->g.width-4, gw->g.y + gw2obj->top * ((gw->g.height-2)-bar_height) / max_scroll_value, 2, bar_height, ps->edge); - } - } - } else if ((gw2obj->cnt > (gw->g.height-2) / iheight) || (gw->g.flags & GLIST_FLG_SCROLLALWAYS)) { - iwidth = gw->g.width - (SCROLLWIDTH+3); - gdispGFillArea(gw->g.display, gw->g.x+iwidth+2, gw->g.y+1, SCROLLWIDTH, gw->g.height-2, gdispBlendColor(ps->fill, gw->pstyle->background, 128)); - gdispGDrawLine(gw->g.display, gw->g.x+iwidth+1, gw->g.y+1, gw->g.x+iwidth+1, gw->g.y+gw->g.height-2, ps->edge); - #if GDISP_NEED_CONVEX_POLYGON - gdispGFillConvexPoly(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+(ARROW/2+1), upArrow, 3, ps->fill); - gdispGFillConvexPoly(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+gw->g.height-(ARROW+ARROW/2+1), downArrow, 3, ps->fill); - #else - #warning "GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on" - gdispGFillArea(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+(ARROW/2+1), ARROW, ARROW, ps->fill); - gdispGFillArea(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+gw->g.height-(ARROW+ARROW/2+1), ARROW, ARROW, ps->fill); - #endif - } else - iwidth = gw->g.width - 2; - - #if GWIN_NEED_LIST_IMAGES - if ((gw->g.flags & GLIST_FLG_HASIMAGES)) { - x += iheight; - iwidth -= iheight; - } - #endif - - - // Find the top item - for (qi = gfxQueueASyncPeek(&gw2obj->list_head), i = iheight - 1; i < gw2obj->top && qi; qi = gfxQueueASyncNext(qi), i+=iheight); - - // the list frame - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, ps->edge); - - // Set the clipping region so we do not override the frame. - gdispGSetClip(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2); - - // Draw until we run out of room or items - for (y = 1-(gw2obj->top%iheight); y < gw->g.height-2 && qi; qi = gfxQueueASyncNext(qi), y += iheight) { - fill = (qi2li->flags & GLIST_FLG_SELECTED) ? ps->fill : gw->pstyle->background; - gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, iheight, fill); - #if GWIN_NEED_LIST_IMAGES - if ((gw->g.flags & GLIST_FLG_HASIMAGES)) { - // Clear the image area - if (qi2li->pimg && gdispImageIsOpen(qi2li->pimg)) { - // Calculate which image - sy = (qi2li->flags & GLIST_FLG_SELECTED) ? 0 : (iheight-VERTICAL_PADDING); - if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) - sy += 2*(iheight-VERTICAL_PADDING); - while (sy > qi2li->pimg->height) - sy -= iheight-VERTICAL_PADDING; - // Draw the image - gdispImageSetBgColor(qi2li->pimg, fill); - gdispGImageDraw(gw->g.display, qi2li->pimg, gw->g.x+1, gw->g.y+y, iheight-VERTICAL_PADDING, iheight-VERTICAL_PADDING, 0, sy); - } - } - #endif - gdispGFillStringBox(gw->g.display, gw->g.x+x+HORIZONTAL_PADDING, gw->g.y+y, iwidth-HORIZONTAL_PADDING, iheight, qi2li->text, gw->g.font, ps->text, fill, justifyLeft); - } - - // Fill any remaining item space - if (y < gw->g.height-1) - gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, gw->g.height-1-y, gw->pstyle->background); -} - -#endif // GFX_USE_GWIN && GWIN_NEED_LIST diff --git a/src/gwin/list.h b/src/gwin/list.h deleted file mode 100644 index d9979993..00000000 --- a/src/gwin/list.h +++ /dev/null @@ -1,304 +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/list.h - * @brief GWIN list widget header file - * - * @defgroup List List - * @ingroup Widgets - * - * @details GWIN allows it to create a list widget. - * - * @pre GFX_USE_GDISP must be set to TRUE in your gfxconf.h - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GDISP_NEED_TEXT must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_LIST must be set to TRUE in your gfxconf.h - * @pre The font you want to use must be enabled in your gfxconf.h - * - * @{ - */ - -#ifndef _GWIN_LIST_H -#define _GWIN_LIST_H - -// This file is included within "gwin/gwin.h" - -/** - * @brief The event type for a list event - */ -#define GEVENT_GWIN_LIST (GEVENT_GWIN_CTRL_FIRST+4) - -/** - * @brief A list event - */ -typedef struct GEventGWinList { - GEventType type; // The type of this event (GEVENT_GWIN_LIST) - GHandle gwin; // The list - #if GWIN_WIDGET_TAGS - WidgetTag tag; // The list tag - #endif - int item; // The item that has been selected (or unselected in a multi-select listbox) -} GEventGWinList; - -// A list window -typedef struct GListObject { - GWidgetObject w; - - #if GINPUT_NEED_MOUSE - coord_t start_mouse_x; - coord_t start_mouse_y; - coord_t last_mouse_y; - #endif - #if GINPUT_NEED_TOGGLE - uint16_t t_up; - uint16_t t_dn; - #endif - - int cnt; // Number of items currently in the list (quicker than counting each time) - int top; // Viewing offset in pixels from the top of the list - gfxQueueASync list_head; // The list of items -} GListObject; - -/** - * @brief Enum to change the behaviour of the scroll bar - * - * @note Used with @p gwinListSetScroll() - * @note @p scrollAlways always show the scrollbar - * @note @p scrollAuto show the scrollbar when there are more items on the list then fit on the screen - * @note @p scrollSmooth enable touch screen smooth scrolling - */ -typedef enum scroll_t { scrollAlways, scrollAuto, scrollSmooth } scroll_t; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create a list widget - * - * @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 Black and White. - * @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 not display anything. - * @note A list remembers its normal drawing state. If there is a window manager then it is automatically - * redrawn if the window is moved or its visibility state is changed. - * @note The list contains no elements after creation. - * @note A slider supports mouse, toggle. Note: toggle only works correctly for single-select lists. - * @note When assigning a toggle, only one toggle is supported per role. If you try to assign more than - * one toggle to a role, it will forget the previous toggle. Two roles are supported: - * Role 0 = toggle for down, role 1 = toggle for up - * - * @param[in] g The GDisplay to display this window on - * @param[in] widget The GListObject structure to initialize. If this is NULL, the structure is dynamically allocated. - * @param[in] pInit The initialization parameters to use - * @param[in] multiselect If TRUE the list is multi-select instead of single-select. - * - * @return NULL if there is no resulting drawing area, otherwise a window handle. - * - * @api - */ -GHandle gwinGListCreate(GDisplay *g, GListObject *widget, GWidgetInit *pInit, bool_t multiselect); -#define gwinListCreate(w, pInit, m) gwinGListCreate(GDISP, w, pInit, m) - -/** - * @brief Enable or disable the rendering of the list - * - * @details Usually the list is being re-rendered when an item is added to the list. This can cause - * flickering and performance issues when many items are added at once. This can be prevented - * by temporarely disabling the render using this function. - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] ena TRUE or FALSE - * - * @api - */ -void gwinListEnableRender(GHandle gh, bool_t ena); - -/** - * @brief Change the behaviour of the scroll bar - * - * @note Current possible values: @p scrollAlways, @p scrollAuto and @p scrollSmooth - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] flag The behaviour to be set - * - * @api - */ -void gwinListSetScroll(GHandle gh, scroll_t flag); - -/** - * @brief Add an item to the list - * - * @note The ID you get returned is not static. If items get removed from the list, the list items get - * reordered. - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] item The string which shall be displayed in the list afterwards - * @param[in] useAlloc If set to TRUE, the string will be dynamically allocated. A static buffer must be passed otherwise - * - * @return The current ID of the item. The ID might change if you remove items from the middle of the list - * - * @api - */ -int gwinListAddItem(GHandle gh, const char* item, bool_t useAlloc); - -/** - * @brief Get the name behind an item with a given ID - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] item The item ID - * - * @return The string of the list item or NULL on error - * - * @api - */ -const char* gwinListItemGetText(GHandle gh, int item); - -/** - * @brief Get the ID of an item with a given name - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] text The item name - * - * @return The id of the list item or -1 on error - * - * @api - */ -int gwinListFindText(GHandle gh, const char* text); - -/** - * @brief Set the custom parameter of an item with a given ID - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] item The item ID - * @param[in] param The parameter to be set - * - * @api - */ -void gwinListItemSetParam(GHandle gh, int item, uint16_t param); - -/** - * @brief Get the custom parameter of an item with a given ID - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] item The item ID - * - * @return The parameter - * - * @api - */ -uint16_t gwinListItemGetParam(GHandle gh, int item); - -/** - * @brief Delete all the items of the list - * - * @param[in] gh The widget handle (must be a list handle) - * - * @api - */ -void gwinListDeleteAll(GHandle gh); - -/** - * @brief Delete an item from the list - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] item The item ID - * - * @api - */ -void gwinListItemDelete(GHandle gh, int item); - -/** - * @brief Get the amount of items within the list - * - * @param[in] gh The widget handle (must be a list handle) - * - * @return The amount of items in the list - * - * @api - */ -int gwinListItemCount(GHandle gh); - -/** - * @brief Check if an item with a given ID is selected - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] item The item ID - * - * @return TRUE if the item is selected, FALSE otherwise - * - * @api - */ -bool_t gwinListItemIsSelected(GHandle gh, int item); - -/** - * @brief Get the ID of the selected item - * - * @param[in] gh The widget handle (must be a list handle) - * - * @return The ID of the selected list item for a single-select list. - * - * @note It always returns -1 (nothing selected) for a multi-select list. - * Instead use @p gwinListItemIsSelected() to get the selection status - * for a multi-select list. - * - * @api - */ -int gwinListGetSelected(GHandle gh); - -/** - * @brief Get the text of the selected item - * - * @param[in] gh The widget handle (must be a list handle) - * - * @return The test of the selected list item for a single-select list. - * - * @note It always returns NULL (nothing selected) for a multi-select list. - * - * @api - */ -const char* gwinListGetSelectedText(GHandle gh); - -#if GWIN_NEED_LIST_IMAGES || defined(__DOXYGEN__) - /** - * @brief Set the image for a list item - * - * @pre GWIN_NEED_LIST_IMAGES must be set to true in your gfxconf.h - * - * @param[in] gh The widget handle (must be a list handle) - * @param[in] item The item ID - * @param[in] pimg The image to be displayed or NULL to turn off the image for this list item. - * - * @note The image should be up to 4 x (the font height) and a width of (the font height). - * The 1st (top) part of the image is displayed for a selected item. - * The 2nd part of the image is displayed for an unselected item. - * The 3rd part of the image is displayed for a disabled selected item. - * The 4th part of the image is displayed for a disabled unselected item. - * If the image is less than 4 times the font height then the image use is collapsed - * into the available height. For example, an image that equals the font height will use - * the same image for all four states. - * @note The image is only displayed while it is open. It is up to the application to open - * the image. - * @note The same image can be used on more than one list item. - * @note Images are aligned with the top (not the baseline) of the list item. - * @note When any item in the list has an image attached, space is allocated to display - * the images even if the image is closed or has been removed by calling @p gwinListItemSetImage() - * with a NULL image or by calling @p gwinListItemDelete(). The only way to turn-off the image area - * for this list is to call gwinListDeleteAll(). - * - */ - void gwinListItemSetImage(GHandle gh, int item, gdispImage *pimg); -#endif - -#ifdef __cplusplus -} -#endif - -#endif // _GWIN_LIST_H -/** @} */ - diff --git a/src/gwin/progressbar.c b/src/gwin/progressbar.c deleted file mode 100644 index 90be6e43..00000000 --- a/src/gwin/progressbar.c +++ /dev/null @@ -1,316 +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/progressbar.c - * @brief GWIN sub-system progressbar code - */ - -#include "gfx.h" - -#if (GFX_USE_GWIN && GWIN_NEED_PROGRESSBAR) || defined(__DOXYGEN__) - -#include "src/gwin/class_gwin.h" - -// Reset the display position back to the value predicted by the saved progressbar position -static void ResetDisplayPos(GProgressbarObject *gsw) { - if (gsw->w.g.width < gsw->w.g.height) - gsw->dpos = gsw->w.g.height-1-((gsw->w.g.height-1)*(gsw->pos-gsw->min))/(gsw->max-gsw->min); - else - gsw->dpos = ((gsw->w.g.width-1)*(gsw->pos-gsw->min))/(gsw->max-gsw->min); -} - -// We have to deinitialize the timer which auto updates the progressbar if any -static void _destroy(GHandle gh) { - #if GWIN_PROGRESSBAR_AUTO - gtimerDeinit( &((GProgressbarObject *)gh)->gt ); - #endif - - _gwidgetDestroy(gh); -} - -// The progressbar VMT table -static const gwidgetVMT progressbarVMT = { - { - "Progressbar", // The classname - sizeof(GProgressbarObject), // The object size - _destroy, // The destroy routine - _gwidgetRedraw, // The redraw routine - 0, // The after-clear routine - }, - gwinProgressbarDraw_Std, // The default drawing routine - #if GINPUT_NEED_MOUSE - { - 0, // Process mouse down events (NOT USED) - 0, // Process mouse up events - 0, // Process mouse move events - }, - #endif - #if GINPUT_NEED_TOGGLE - { - 0, // 1 toggle role - 0, // Assign Toggles - 0, // Get Toggles - 0, // Process toggle off events (NOT USED) - 0, // Process toggle on events - }, - #endif - #if GINPUT_NEED_DIAL - { - 0, // 1 dial roles - 0, // Assign Dials - 0, // Get Dials - 0, // Process dial move events - }, - #endif -}; - -GHandle gwinGProgressbarCreate(GDisplay *g, GProgressbarObject *gs, const GWidgetInit *pInit) { - if (!(gs = (GProgressbarObject *)_gwidgetCreate(g, &gs->w, pInit, &progressbarVMT))) - return 0; - - gs->min = 0; - gs->max = 100; - gs->res = 1; - gs->pos = 0; - - #if GWIN_PROGRESSBAR_AUTO - gs->delay = 0; - gtimerInit(&gs->gt); - #endif - - ResetDisplayPos(gs); - gwinSetVisible((GHandle)gs, pInit->g.show); - - return (GHandle)gs; -} - -void gwinProgressbarSetRange(GHandle gh, int min, int max) { - #define gsw ((GProgressbarObject *)gh) - - if (gh->vmt != (gwinVMT *)&progressbarVMT) - return; - - if (min == max) // prevent divide by 0 errors. - max++; - gsw->min = min; - gsw->max = max; - gsw->pos = min; - - ResetDisplayPos(gsw); - - #undef gsw -} - -void gwinProgressbarSetPosition(GHandle gh, int pos) { - #define gsw ((GProgressbarObject *)gh) - - if (gh->vmt != (gwinVMT *)&progressbarVMT) - return; - - if (gsw->min <= gsw->max) { - if (pos < gsw->min) gsw->pos = gsw->min; - else if (pos > gsw->max) gsw->pos = gsw->max; - else gsw->pos = pos; - } else { - if (pos > gsw->min) gsw->pos = gsw->min; - else if (pos < gsw->max) gsw->pos = gsw->max; - else gsw->pos = pos; - } - - ResetDisplayPos(gsw); - _gwinUpdate(gh); - - #undef gsw -} - -void gwinProgressbarSetResolution(GHandle gh, int resolution) { - #define gsw ((GProgressbarObject *)gh) - - if (gh->vmt != (gwinVMT *)&progressbarVMT) - return; - - if (resolution <= 0) - resolution = 1; - - gsw->res = resolution; - - #undef gsw -} - -void gwinProgressbarIncrement(GHandle gh) { - #define gsw ((GProgressbarObject *)gh) - - if (gh->vmt != (gwinVMT *)&progressbarVMT) - return; - - if (gsw->max - gsw->pos > gsw->res) - gsw->pos += gsw->res; - else - gsw->pos = gsw->max; - - ResetDisplayPos(gsw); - _gwinUpdate(gh); - - #undef gsw -} - -void gwinProgressbarDecrement(GHandle gh) { - #define gsw ((GProgressbarObject *)gh) - - if (gh->vmt != (gwinVMT *)&progressbarVMT) - return; - - if (gsw->pos > gsw->res) - gsw->pos -= gsw->min; - else - gsw->pos = gsw->min; - - gsw->pos -= gsw->res; - - ResetDisplayPos(gsw); - _gwinUpdate(gh); - - #undef gsw -} - -#if GWIN_PROGRESSBAR_AUTO - // used by gwinProgressbarStart(); - static void _progressbarCallback(void *param) { - #define gsw ((GProgressbarObject *)gh) - GHandle gh = (GHandle)param; - - if (gh->vmt != (gwinVMT *)&progressbarVMT) - return; - - gwinProgressbarIncrement(gh); - - if (gsw->pos < gsw->max) - gtimerStart(&(gsw->gt), _progressbarCallback, gh, FALSE, gsw->delay); - - #undef gsw - } - - void gwinProgressbarStart(GHandle gh, delaytime_t delay) { - #define gsw ((GProgressbarObject *)gh) - - if (gh->vmt != (gwinVMT *)&progressbarVMT) - return; - - gsw->delay = delay; - - gtimerStart(&(gsw->gt), _progressbarCallback, gh, FALSE, gsw->delay); - - #undef gsw - } - - void gwinProgressbarStop(GHandle gh) { - #define gsw ((GProgressbarObject *)gh) - - if (gh->vmt != (gwinVMT *)&progressbarVMT) - return; - - gtimerStop(&(gsw->gt)); - - #undef gsw - } -#endif /* GWIN_PROGRESSBAR_AUTO */ - -/*---------------------------------------------------------- - * Custom Draw Routines - *----------------------------------------------------------*/ - -void gwinProgressbarDraw_Std(GWidgetObject *gw, void *param) { - #define gsw ((GProgressbarObject *)gw) - - const GColorSet * pcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&progressbarVMT) - return; - - // get the colors right - if ((gw->g.flags & GWIN_FLG_SYSENABLED)) - pcol = &gw->pstyle->pressed; - else - pcol = &gw->pstyle->disabled; - - // Vertical progressbar - if (gw->g.width < gw->g.height) { - if (gsw->dpos != gw->g.height-1) - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.width, gw->g.height - gsw->dpos, pcol->progress); // Active Area - if (gsw->dpos != 0) - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, gw->pstyle->enabled.progress); // Inactive area - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb - - // Horizontal progressbar - } else { - if (gsw->dpos != gw->g.width-1) - gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.width-gsw->dpos, gw->g.height, gw->pstyle->enabled.progress); // Inactive area - if (gsw->dpos != 0) - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gsw->dpos, gw->g.height, pcol->progress); // Active Area - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge - gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb - } - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - - #undef gsw -} - -#if GDISP_NEED_IMAGE -void gwinProgressbarDraw_Image(GWidgetObject *gw, void *param) { - #define gsw ((GProgressbarObject *)gw) - #define gi ((gdispImage *)param) - const GColorSet * pcol; - coord_t z, v; - - if (gw->g.vmt != (gwinVMT *)&progressbarVMT) - return; - - if ((gw->g.flags & GWIN_FLG_SYSENABLED)) - pcol = &gw->pstyle->pressed; - else - pcol = &gw->pstyle->disabled; - - if (gw->g.width < gw->g.height) { // Vertical progressbar - if (gsw->dpos != 0) // The unfilled area - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, gw->pstyle->enabled.progress); // Inactive area - if (gsw->dpos != gw->g.height-1) { // The filled area - for(z=gw->g.height, v=gi->height; z > gsw->dpos;) { - z -= v; - if (z < gsw->dpos) { - v -= gsw->dpos - z; - z = gsw->dpos; - } - gdispGImageDraw(gw->g.display, gi, gw->g.x, gw->g.y+z, gw->g.width, v, 0, gi->height-v); - } - } - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb - - // Horizontal progressbar - } else { - if (gsw->dpos != gw->g.width-1) // The unfilled area - gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.width-gsw->dpos, gw->g.height, gw->pstyle->enabled.progress); // Inactive area - if (gsw->dpos != 0) { // The filled area - for(z=0, v=gi->width; z < gsw->dpos; z += v) { - if (z+v > gsw->dpos) - v -= z+v - gsw->dpos; - gdispGImageDraw(gw->g.display, gi, gw->g.x+z, gw->g.y, v, gw->g.height, 0, 0); - } - } - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge - gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb - } - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - - #undef gsw -} -#endif /* GDISP_NEED_IMAGE */ - -#endif /* GFX_USE_GWIN && GWIN_NEED_BUTTON */ diff --git a/src/gwin/progressbar.h b/src/gwin/progressbar.h deleted file mode 100644 index 4dadf904..00000000 --- a/src/gwin/progressbar.h +++ /dev/null @@ -1,209 +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/progressbar.h - * @brief GWIN Graphic window subsystem header file. - * - * @defgroup Progressbar Progressbar - * @ingroup Widgets - * - * @details Create progressbars with different styles - * - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_PROGRESSBAR must be set to TRUE in your gfxconf.h - * @{ - */ - -#ifndef _GWIN_PROGRESSBAR_H -#define _GWIN_PROGRESSBAR_H - -// A progressbar window -typedef struct GProgressbarObject { - GWidgetObject w; - coord_t dpos; - int min; - int max; - int res; - int pos; - #if GWIN_PROGRESSBAR_AUTO - GTimer gt; - delaytime_t delay; - #endif -} GProgressbarObject; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create a progressbar 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] gb The GProgressbarObject structure to initialise. If this is NULL the structure is dynamically allocated. - * @param[in] pInit The initialization parameters to use - * - * @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 progressbar remembers its normal drawing state. If there is a window manager then it is automatically - * redrawn if the window is moved or its visibility state is changed. - * @note The initial progressbar range is from 0 to 100 with an initial position of 0. - * @note A progressbar does not take any GINPUT inputs. - * - * @api - */ -GHandle gwinGProgressbarCreate(GDisplay *g, GProgressbarObject *gb, const GWidgetInit *pInit); -#define gwinProgressbarCreate(w, pInit) gwinGProgressbarCreate(GDISP, w, pInit) - -/** - * @brief Set the progressbar range. - * - * @param[in] gh The window handle (must be a progressbar window) - * @param[in] min The minimum value - * @param[in] max The maximum value - * - * @note The defaults are 0 and 100 - * @note Sets the position to the minimum value. - * @note The progressbar is not automatically drawn. Call gwinProgressbarDraw() after changing the range. - * - * @api - */ -void gwinProgressbarSetRange(GHandle gh, int min, int max); - -/** - * @brief Set the progressbar position. - * - * @param[in] gh The window handle (must be a progressbar window) - * @param[in] pos The new position - * - * @note If the new position is outside the progressbar range then the position - * is set to the closest end of the range. - * @note The progressbar is not automatically drawn. Call gwinProgressbarDraw() after changing the position. - * - * @api - */ -void gwinProgressbarSetPosition(GHandle gh, int pos); - -/** - * @brief Set the resolution for the incrementation and decrementation of the progressbar - * - * @note Default is set to 1 - * - * @param[in] gh The window handle (must be a progressbar window) - * @param[in] res The resolution to be set - * - * @api - */ -void gwinProgressbarSetResolution(GHandle gh, int res); - -/** - * @brief Increment the progressbar value - * - * @details Increments by the resolution set through gwinProgressbarSetResolution() - * - * @param[in] gh The window handle (must be a progressbar window) - * - * @api - */ -void gwinProgressbarIncrement(GHandle gh); - -/** - * @brief Decrement the progressbar value - * - * @details Decrements by the resolution set through gwinProgressbarSetResolution() - * - * @param[in] gh The window handle (must be a progressbar window) - * - * @api - */ -void gwinProgressbarDecrement(GHandle gh); - -/** - * @brief Get the current progressbar position. - * @return The progressbar position - * - * @param[in] gh The window handle (must be a progressbar window) - * - * @note The use of a listener to get the progressbar position is recommended if you - * want continuous updates on the progressbar position. - * - * @api - */ -#define gwinProgressbarGetPosition(gh) (((GProgressbarObject *)(gh))->pos) - - /** - * @brief Reset the progressbar to the minimum position - * - * @param[in] gh The window handle (must be a progressbar window) - * - * @api - */ -#define gwinProgressbarReset(gh) gwinProgressbarSetPosition(gh, ((GProgressbarObject *)(gh))->min) - -#if GWIN_PROGRESSBAR_AUTO - /** - * @brief Automatically increments the progress bar - * - * @note The delay is generated using the GTIMER module which is based on software/virtual timer. - * Therefore, the delay is totally unprecise. - * - * @note The progressbar incrementation starts at the current level. It is not reset to the minimum value. - * - * @note An event is generated once the maximum value has been reached (ToDo) - * - * @param[in] gh The window handle (must be a progressbar window) - * @param[in] delay The incrementation delay (in milliseconds) - * - * @api - */ - void gwinProgressbarStart(GHandle gh, delaytime_t delay); - - /** - * @brief Stop the timer which is started by @p gwinProgressbarStart() - * - * @param[in] gh The window handle (must be a progressbar window) - * - * @api - */ - void gwinProgressbarStop(GHandle gh); -#endif /* GWIN_PROGRESSBAR_AUTO */ - -/** - * @brief Some custom progressbar drawing routines - * @details These function may be passed to @p gwinSetCustomDraw() to get different progressbar drawing styles - * - * @param[in] gw The widget (which must be a progressbar) - * @param[in] param A parameter passed in from the user - * - * @note In your custom progressbar drawing function you may optionally call this - * standard functions and then draw your extra details on top. - * @note The standard functions below ignore the param parameter except for @p gwinProgressbarDraw_Image(). - * @note The image custom draw function @p gwinProgressbarDraw_Image() uses param to pass in the gdispImage pointer. - * The image must be already opened before calling @p gwinSetCustomDraw(). The image is tiled to fill - * the active area of the progressbar. The normal colors apply to the border and inactive area and the dividing line - * between the active and inactive areas. - * No checking is done to compare the dimensions of the progressbar to the size of the image. - * Note text is drawn on top of the image. - * @note These custom drawing routines don't have to worry about setting clipping as the framework - * sets clipping to the object window prior to calling these routines. - * - * @api - * @{ - */ -void gwinProgressbarDraw_Std(GWidgetObject *gw, void *param); -void gwinProgressbarDraw_Image(GWidgetObject *gw, void *param); -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif /* _GWIN_PROGRESSBAR_H */ -/** @} */ diff --git a/src/gwin/radio.c b/src/gwin/radio.c deleted file mode 100644 index 4799c2a4..00000000 --- a/src/gwin/radio.c +++ /dev/null @@ -1,280 +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/radio.c - * @brief GWIN sub-system radio button code - */ - -#include "gfx.h" - -#if GFX_USE_GWIN && GWIN_NEED_RADIO - -#include "src/gwin/class_gwin.h" - -#define GRADIO_TAB_CNR 8 // Diagonal corner on active tab -#define GRADIO_TOP_FADE 50 // (GRADIO_TOP_FADE/255)% fade to white for top of tab/button -#define GRADIO_BOTTOM_FADE 25 // (GRADIO_BOTTOM_FADE/255)% fade to black for bottom of tab/button -#define GRADIO_OUTLINE_FADE 128 // (GRADIO_OUTLINE_FADE/255)% fade to background for active tab edge - -// Our pressed state -#define GRADIO_FLG_PRESSED (GWIN_FIRST_CONTROL_FLAG<<0) - -// Send the button event -static void SendRadioEvent(GWidgetObject *gw) { - GSourceListener * psl; - GEvent * pe; - #define pbe ((GEventGWinRadio *)pe) - - // Trigger a GWIN Button Event - psl = 0; - while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { - if (!(pe = geventGetEventBuffer(psl))) - continue; - pbe->type = GEVENT_GWIN_RADIO; - pbe->gwin = (GHandle)gw; - pbe->group = ((GRadioObject *)gw)->group; - #if GWIN_WIDGET_TAGS - pbe->tag = gw->tag; - #endif - geventSendEvent(psl); - } - - #undef pbe -} - -#if GINPUT_NEED_MOUSE - // A mouse down has occurred over the button - static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { - (void) x; (void) y; - - gwinRadioPress((GHandle)gw); - } -#endif - -#if GINPUT_NEED_TOGGLE - // A toggle on has occurred - static void ToggleOn(GWidgetObject *gw, uint16_t role) { - (void) role; - - gwinRadioPress((GHandle)gw); - } - - static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { - (void) role; - ((GRadioObject *)gw)->toggle = instance; - } - - static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { - (void) role; - return ((GRadioObject *)gw)->toggle; - } -#endif - -// The radio button VMT table -static const gwidgetVMT radioVMT = { - { - "Radio", // The classname - sizeof(GRadioObject), // The object size - _gwidgetDestroy, // The destroy routine - _gwidgetRedraw, // The redraw routine - 0, // The after-clear routine - }, - gwinRadioDraw_Radio, // The default drawing routine - #if GINPUT_NEED_MOUSE - { - MouseDown, // Process mouse down events - 0, // Process mouse up events (NOT USED) - 0, // Process mouse move events (NOT USED) - }, - #endif - #if GINPUT_NEED_TOGGLE - { - 1, // 1 toggle role - ToggleAssign, // Assign Toggles - ToggleGet, // Get Toggles - 0, // Process toggle off events (NOT USED) - ToggleOn, // Process toggle on events - }, - #endif - #if GINPUT_NEED_DIAL - { - 0, // No dial roles - 0, // Assign Dials (NOT USED) - 0, // Get Dials (NOT USED) - 0, // Process dial move events (NOT USED) - }, - #endif -}; - -GHandle gwinGRadioCreate(GDisplay *g, GRadioObject *gw, const GWidgetInit *pInit, uint16_t group) { - if (!(gw = (GRadioObject *)_gwidgetCreate(g, &gw->w, pInit, &radioVMT))) - return 0; - - #if GINPUT_NEED_TOGGLE - gw->toggle = GWIDGET_NO_INSTANCE; - #endif - gw->group = group; - gwinSetVisible((GHandle)gw, pInit->g.show); - return (GHandle)gw; -} - -void gwinRadioPress(GHandle gh) { - GHandle gx; - - if (gh->vmt != (gwinVMT *)&radioVMT || (gh->flags & GRADIO_FLG_PRESSED)) - return; - - if ((gx = gwinRadioGetActive(((GRadioObject *)gh)->group))) { - gx->flags &= ~GRADIO_FLG_PRESSED; - _gwinUpdate(gx); - } - gh->flags |= GRADIO_FLG_PRESSED; - _gwinUpdate(gh); - SendRadioEvent((GWidgetObject *)gh); -} - -bool_t gwinRadioIsPressed(GHandle gh) { - if (gh->vmt != (gwinVMT *)&radioVMT) - return FALSE; - - return (gh->flags & GRADIO_FLG_PRESSED) ? TRUE : FALSE; -} - -GHandle gwinRadioGetActive(uint16_t group) { - GHandle gh; - - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - if (gh->vmt == (gwinVMT *)&radioVMT && ((GRadioObject *)gh)->group == group && (gh->flags & GRADIO_FLG_PRESSED)) - return gh; - } - return 0; -} - -/*---------------------------------------------------------- - * Custom Draw Routines - *----------------------------------------------------------*/ - -static const GColorSet *getDrawColors(GWidgetObject *gw) { - if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled; - if ((gw->g.flags & GRADIO_FLG_PRESSED)) return &gw->pstyle->pressed; - return &gw->pstyle->enabled; -} - -void gwinRadioDraw_Radio(GWidgetObject *gw, void *param) { - #define gcw ((GRadioObject *)gw) - coord_t ld, df; - const GColorSet * pcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&radioVMT) return; - pcol = getDrawColors(gw); - - ld = gw->g.width < gw->g.height ? gw->g.width : gw->g.height; - - #if GDISP_NEED_CIRCLE - df = (ld-1)/2; - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, ld, ld, gw->pstyle->background); - gdispGDrawCircle(gw->g.display, gw->g.x+df, gw->g.y+df, df, pcol->edge); - - if (gw->g.flags & GRADIO_FLG_PRESSED) - gdispGFillCircle(gw->g.display, gw->g.x+df, gw->g.y+df, df <= 2 ? 1 : (df-2), pcol->fill); - #else - gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+1, ld, ld-2, gw->pstyle->background); - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, ld, ld, pcol->edge); - - df = ld < 4 ? 1 : 2; - if (gw->g.flags & GRADIO_FLG_PRESSED) - gdispGFillArea(gw->g.display, gw->g.x+df, gw->g.y+df, ld-2*df, ld-2*df, pcol->fill); - #endif - - gdispGFillStringBox(gw->g.display, gw->g.x+ld+1, gw->g.y, gw->g.width-ld-1, gw->g.height, gw->text, gw->g.font, pcol->text, gw->pstyle->background, justifyLeft); - #undef gcw -} - -#if GWIN_FLAT_STYLING - void gwinRadioDraw_Button(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&radioVMT) return; - pcol = getDrawColors(gw); - - gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); - gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); - } - void gwinRadioDraw_Tab(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&radioVMT) return; - pcol = getDrawColors(gw); - - if ((gw->g.flags & GRADIO_FLG_PRESSED)) { - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); - gdispGFillStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); - } else { - gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); - gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); - } - } -#else - void gwinRadioDraw_Button(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - fixed alpha; - fixed dalpha; - coord_t i; - color_t tcol, bcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&radioVMT) return; - pcol = getDrawColors(gw); - - /* Fill the box blended from variants of the fill color */ - tcol = gdispBlendColor(White, pcol->fill, GRADIO_TOP_FADE); - bcol = gdispBlendColor(Black, pcol->fill, GRADIO_BOTTOM_FADE); - dalpha = FIXED(255)/gw->g.height; - for(alpha = 0, i = 0; i < gw->g.height; i++, alpha += dalpha) - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+i, gw->g.x+gw->g.width-2, gw->g.y+i, gdispBlendColor(bcol, tcol, NONFIXED(alpha))); - - gdispGDrawStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, justifyCenter); - gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); - } - void gwinRadioDraw_Tab(GWidgetObject *gw, void *param) { - const GColorSet * pcol; - fixed alpha; - fixed dalpha; - coord_t i; - color_t tcol, bcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&radioVMT) return; - pcol = getDrawColors(gw); - - if ((gw->g.flags & GRADIO_FLG_PRESSED)) { - tcol = gdispBlendColor(pcol->edge, gw->pstyle->background, GRADIO_OUTLINE_FADE); - gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->text, gw->g.font, pcol->text, gw->g.bgcolor, justifyCenter); - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y, gw->g.x+gw->g.width-(GRADIO_TAB_CNR+1), gw->g.y, tcol); - gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-(GRADIO_TAB_CNR+1), gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+GRADIO_TAB_CNR, tcol); - gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y+GRADIO_TAB_CNR, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, tcol); - } else { - /* Fill the box blended from variants of the fill color */ - tcol = gdispBlendColor(White, pcol->fill, GRADIO_TOP_FADE); - bcol = gdispBlendColor(Black, pcol->fill, GRADIO_BOTTOM_FADE); - dalpha = FIXED(255)/gw->g.height; - for(alpha = 0, i = 0; i < gw->g.height; i++, alpha += dalpha) - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+i, gw->g.x+gw->g.width-2, gw->g.y+i, gdispBlendColor(bcol, tcol, NONFIXED(alpha))); - gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - } - } -#endif - -#endif /* GFX_USE_GWIN && GWIN_NEED_BUTTON */ diff --git a/src/gwin/radio.h b/src/gwin/radio.h deleted file mode 100644 index e13962c7..00000000 --- a/src/gwin/radio.h +++ /dev/null @@ -1,144 +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/radio.h - * @brief GWIN Graphic window subsystem header file. - * - * @defgroup RadioButton RadioButton - * @ingroup Widgets - * - * @details GWIN allows it to easily create radio buttons with different styles. - * - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_RADIO must be set to TRUE in your gfxconf.h - * @{ - */ - -#ifndef _GWIN_RADIO_H -#define _GWIN_RADIO_H - -/* This file is included within "gwin/gwidget.h" */ - -/** - * @brief The Event Type for a Radio Event - */ -#define GEVENT_GWIN_RADIO (GEVENT_GWIN_CTRL_FIRST+3) - -/** - * @brief A Button Event - * @note There are currently no GEventGWinRadio listening flags - use 0 as the flags to @p gwinAttachListener() - */ -typedef struct GEventGWinRadio { - GEventType type; // The type of this event (GEVENT_GWIN_RADIO) - GHandle gwin; // The radio button that has been depressed - #if GWIN_WIDGET_TAGS - WidgetTag tag; // The radio tag - #endif - uint16_t group; // The group for this radio button -} GEventGWinRadio; - -/** - * @brief The radio button widget structure - * @note Do not use the members directly - treat it as a black-box. - */ -typedef struct GRadioObject { - GWidgetObject w; - #if GINPUT_NEED_TOGGLE - uint16_t toggle; - #endif - uint16_t group; -} GRadioObject; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create a radio widget. - * @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] gb The GRadioObject structure to initialise. If this is NULL the structure is dynamically allocated. - * @param[in] pInit The initialisation parameters - * @param[in] group The group of radio buttons this radio button belongs to. - * - * @note Only one radio button in any group is ever pressed at one time. Pressing one radio button will - * release all others in the group. - * @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 radio button remembers its normal drawing state. If there is a window manager then it is automatically - * redrawn if the window is moved or its visibility state is changed. - * @note A radio button supports mouse and a toggle input. - * @note When assigning a toggle, only one toggle is supported. If you try to assign more than one toggle it will - * forget the previous toggle. When assigning a toggle the role parameter must be 0. - * - * @api - */ -GHandle gwinGRadioCreate(GDisplay *g, GRadioObject *gb, const GWidgetInit *pInit, uint16_t group); -#define gwinRadioCreate(w, pInit, gr) gwinGRadioCreate(GDISP, w, pInit, gr) - -/** - * @brief Press this radio button (and by definition unset any others in the group) - * - * @param[in] gh The window handle (must be a radio widget) - * - * @api - */ -void gwinRadioPress(GHandle gh); - -/** - * @brief Is the radio button currently pressed - * @return TRUE if the button is pressed - * - * @param[in] gh The window handle (must be a radio widget) - * - * @api - */ -bool_t gwinRadioIsPressed(GHandle gh); - -/** - * @brief Find the currently pressed radio button in the specified group - * @return The handle of the pressed radio button or NULL if none are pressed - * - * @param[in] group The radio button group to be examined - * - * @return The handle of the currently pressed radio button - * - * @api - */ -GHandle gwinRadioGetActive(uint16_t group); - -/** - * @brief Some custom radio button drawing routines - * @details These function may be passed to @p gwinSetCustomDraw() to get different radio button drawing styles - * - * @param[in] gw The widget object (in this case a radio button) - * @param[in] param A parameter passed in from the user - * - * @note In your custom radio drawing function you may optionally call these - * standard functions and then draw your extra details on top. - * @note The standard functions below ignore the param parameter. - * @note These custom drawing routines don't have to worry about setting clipping as the framework - * sets clipping to the object window prior to calling these routines. - * - * @api - * @{ - */ -void gwinRadioDraw_Radio(GWidgetObject *gw, void *param); // @< A standard radio button -void gwinRadioDraw_Button(GWidgetObject *gw, void *param); // @< Draw as a button -void gwinRadioDraw_Tab(GWidgetObject *gw, void *param); // @< Draw as a tab -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif /* _GWIN_RADIO_H */ -/** @} */ diff --git a/src/gwin/slider.c b/src/gwin/slider.c deleted file mode 100644 index 2329230c..00000000 --- a/src/gwin/slider.c +++ /dev/null @@ -1,376 +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/slider.c - * @brief GWIN sub-system slider code - */ - -#include "gfx.h" - -#if (GFX_USE_GWIN && GWIN_NEED_SLIDER) || defined(__DOXYGEN__) - -#include "src/gwin/class_gwin.h" - -#ifndef GWIN_SLIDER_DEAD_BAND - #define GWIN_SLIDER_DEAD_BAND 5 -#endif - -#ifndef GWIN_SLIDER_TOGGLE_INC - #define GWIN_SLIDER_TOGGLE_INC 20 // How many toggles to go from minimum to maximum -#endif - -// Send the slider event -static void SendSliderEvent(GWidgetObject *gw) { - GSourceListener * psl; - GEvent * pe; - #define pse ((GEventGWinSlider *)pe) - - // Trigger a GWIN Button Event - psl = 0; - while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { - if (!(pe = geventGetEventBuffer(psl))) - continue; - pse->type = GEVENT_GWIN_SLIDER; - pse->gwin = (GHandle)gw; - pse->position = ((GSliderObject *)gw)->pos; - #if GWIN_WIDGET_TAGS - pse->tag = gw->tag; - #endif - geventSendEvent(psl); - } - - #undef pse -} - -// Reset the display position back to the value predicted by the saved slider position -static void ResetDisplayPos(GSliderObject *gsw) { - if (gsw->w.g.width < gsw->w.g.height) - gsw->dpos = gsw->w.g.height-1-((gsw->w.g.height-1)*(gsw->pos-gsw->min))/(gsw->max-gsw->min); - else - gsw->dpos = ((gsw->w.g.width-1)*(gsw->pos-gsw->min))/(gsw->max-gsw->min); -} - -#if GINPUT_NEED_MOUSE - // A mouse up event - static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) { - #define gsw ((GSliderObject *)gw) - #define gh ((GHandle)gw) - - #if GWIN_BUTTON_LAZY_RELEASE - // Clip to the slider - if (x < 0) x = 0; - else if (x >= gh->width) x = gh->width-1; - if (y < 0) y = 0; - else if (y >= gh->height) x = gh->height-1; - #else - // Are we over the slider? - if (x < 0 || x >= gh->width || y < 0 || y >= gh->height) { - // No - restore the slider - ResetDisplayPos(gsw); - _gwinUpdate(gh); - return; - } - #endif - - // Set the new position - if (gh->width < gh->height) { - if (y > gh->height-GWIN_SLIDER_DEAD_BAND) - gsw->pos = gsw->min; - else if (y < GWIN_SLIDER_DEAD_BAND) - gsw->pos = gsw->max; - else - gsw->pos = (uint16_t)((int32_t)(gh->height-1-y-GWIN_SLIDER_DEAD_BAND)*(gsw->max-gsw->min)/(gh->height-2*GWIN_SLIDER_DEAD_BAND) + gsw->min); - } else { - if (x > gh->width-GWIN_SLIDER_DEAD_BAND) - gsw->pos = gsw->max; - else if (x < GWIN_SLIDER_DEAD_BAND) - gsw->pos = gsw->min; - else - gsw->pos = (uint16_t)((int32_t)(x-GWIN_SLIDER_DEAD_BAND)*(gsw->max-gsw->min)/(gh->width-2*GWIN_SLIDER_DEAD_BAND) + gsw->min); - } - - ResetDisplayPos(gsw); - _gwinUpdate(gh); - - // Generate the event - SendSliderEvent(gw); - #undef gh - #undef gsw - } - - // A mouse move (or mouse down) event - static void MouseMove(GWidgetObject *gw, coord_t x, coord_t y) { - #define gsw ((GSliderObject *)gw) - - // Determine the temporary display position (with range checking) - if (gw->g.width < gw->g.height) { - if (y < 0) - gsw->dpos = 0; - else if (y >= gw->g.height) - gsw->dpos = gw->g.height-1; - else - gsw->dpos = y; - } else { - if (x < 0) - gsw->dpos = 0; - else if (x >= gw->g.width) - gsw->dpos = gw->g.width-1; - else - gsw->dpos = x; - } - - // Update the display - _gwinUpdate(&gw->g); - #undef gsw - } -#endif - -#if GINPUT_NEED_TOGGLE - // A toggle on has occurred - static void ToggleOn(GWidgetObject *gw, uint16_t role) { - #define gsw ((GSliderObject *)gw) - - if (role) { - gwinSliderSetPosition((GHandle)gw, gsw->pos+(gsw->max-gsw->min)/GWIN_SLIDER_TOGGLE_INC); - SendSliderEvent(gw); - } else { - gwinSliderSetPosition((GHandle)gw, gsw->pos-(gsw->max-gsw->min)/GWIN_SLIDER_TOGGLE_INC); - SendSliderEvent(gw); - } - #undef gsw - } - - static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { - if (role) - ((GSliderObject *)gw)->t_up = instance; - else - ((GSliderObject *)gw)->t_dn = instance; - } - - static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { - return role ? ((GSliderObject *)gw)->t_up : ((GSliderObject *)gw)->t_dn; - } -#endif - -#if GINPUT_NEED_DIAL - // A dial move event - static void DialMove(GWidgetObject *gw, uint16_t role, uint16_t value, uint16_t max) { - #define gsw ((GSliderObject *)gw) - (void) role; - - // Set the new position - gsw->pos = (uint16_t)((uint32_t)value*(gsw->max-gsw->min)/max + gsw->min); - - ResetDisplayPos(gsw); - _gwinUpdate((GHandle)gw); - - // Generate the event - SendSliderEvent(gw); - #undef gsw - } - - static void DialAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { - (void) role; - ((GSliderObject *)gw)->dial = instance; - } - - static uint16_t DialGet(GWidgetObject *gw, uint16_t role) { - (void) role; - return ((GSliderObject *)gw)->dial; - } -#endif - -// The slider VMT table -static const gwidgetVMT sliderVMT = { - { - "Slider", // The classname - sizeof(GSliderObject), // The object size - _gwidgetDestroy, // The destroy routine - _gwidgetRedraw, // The redraw routine - 0, // The after-clear routine - }, - gwinSliderDraw_Std, // The default drawing routine - #if GINPUT_NEED_MOUSE - { - 0, // Process mouse down events (NOT USED) - MouseUp, // Process mouse up events - MouseMove, // Process mouse move events - }, - #endif - #if GINPUT_NEED_TOGGLE - { - 2, // 1 toggle role - ToggleAssign, // Assign Toggles - ToggleGet, // Get Toggles - 0, // Process toggle off events (NOT USED) - ToggleOn, // Process toggle on events - }, - #endif - #if GINPUT_NEED_DIAL - { - 1, // 1 dial roles - DialAssign, // Assign Dials - DialGet, // Get Dials - DialMove, // Process dial move events - }, - #endif -}; - -GHandle gwinGSliderCreate(GDisplay *g, GSliderObject *gs, const GWidgetInit *pInit) { - if (!(gs = (GSliderObject *)_gwidgetCreate(g, &gs->w, pInit, &sliderVMT))) - return 0; - #if GINPUT_NEED_TOGGLE - gs->t_dn = GWIDGET_NO_INSTANCE; - gs->t_up = GWIDGET_NO_INSTANCE; - #endif - #if GINPUT_NEED_DIAL - gs->dial = GWIDGET_NO_INSTANCE; - #endif - gs->min = 0; - gs->max = 100; - gs->pos = 0; - ResetDisplayPos(gs); - gwinSetVisible((GHandle)gs, pInit->g.show); - return (GHandle)gs; -} - -void gwinSliderSetRange(GHandle gh, int min, int max) { - #define gsw ((GSliderObject *)gh) - - if (gh->vmt != (gwinVMT *)&sliderVMT) - return; - - if (min == max) // prevent divide by 0 errors. - max++; - gsw->min = min; - gsw->max = max; - gsw->pos = min; - ResetDisplayPos(gsw); - #undef gsw -} - -void gwinSliderSetPosition(GHandle gh, int pos) { - #define gsw ((GSliderObject *)gh) - - if (gh->vmt != (gwinVMT *)&sliderVMT) - return; - - if (gsw->min <= gsw->max) { - if (pos < gsw->min) gsw->pos = gsw->min; - else if (pos > gsw->max) gsw->pos = gsw->max; - else gsw->pos = pos; - } else { - if (pos > gsw->min) gsw->pos = gsw->min; - else if (pos < gsw->max) gsw->pos = gsw->max; - else gsw->pos = pos; - } - ResetDisplayPos(gsw); - _gwinUpdate(gh); - - #undef gsw -} - -/*---------------------------------------------------------- - * Custom Draw Routines - *----------------------------------------------------------*/ - -void gwinSliderDraw_Std(GWidgetObject *gw, void *param) { - #define gsw ((GSliderObject *)gw) - const GColorSet * pcol; - (void) param; - - if (gw->g.vmt != (gwinVMT *)&sliderVMT) - return; - - if ((gw->g.flags & GWIN_FLG_SYSENABLED)) - pcol = &gw->pstyle->pressed; - else - pcol = &gw->pstyle->disabled; - - if (gw->g.width < gw->g.height) { // Vertical slider - if (gsw->dpos != gw->g.height-1) - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.width, gw->g.height - gsw->dpos, pcol->progress); // Active Area - if (gsw->dpos != 0) - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, gw->pstyle->enabled.progress); // Inactive area - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb - if (gsw->dpos >= 2) - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos-2, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos-2, pcol->edge); // Thumb - if (gsw->dpos <= gw->g.height-2) - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos+2, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos+2, pcol->edge); // Thumb - - // Horizontal slider - } else { - if (gsw->dpos != gw->g.width-1) - gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.width-gsw->dpos, gw->g.height, gw->pstyle->enabled.progress); // Inactive area - if (gsw->dpos != 0) - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gsw->dpos, gw->g.height, pcol->progress); // Active Area - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge - gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb - if (gsw->dpos >= 2) - gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos-2, gw->g.y, gw->g.x+gsw->dpos-2, gw->g.y+gw->g.height-1, pcol->edge); // Thumb - if (gsw->dpos <= gw->g.width-2) - gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos+2, gw->g.y, gw->g.x+gsw->dpos+2, gw->g.y+gw->g.height-1, pcol->edge); // Thumb - } - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - - #undef gsw -} - -#if GDISP_NEED_IMAGE -void gwinSliderDraw_Image(GWidgetObject *gw, void *param) { - #define gsw ((GSliderObject *)gw) - #define gi ((gdispImage *)param) - const GColorSet * pcol; - coord_t z, v; - - if (gw->g.vmt != (gwinVMT *)&sliderVMT) - return; - - if ((gw->g.flags & GWIN_FLG_SYSENABLED)) - pcol = &gw->pstyle->pressed; - else - pcol = &gw->pstyle->disabled; - - if (gw->g.width < gw->g.height) { // Vertical slider - if (gsw->dpos != 0) // The unfilled area - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, gw->pstyle->enabled.progress); // Inactive area - if (gsw->dpos != gw->g.height-1) { // The filled area - for(z=gw->g.height, v=gi->height; z > gsw->dpos;) { - z -= v; - if (z < gsw->dpos) { - v -= gsw->dpos - z; - z = gsw->dpos; - } - gdispGImageDraw(gw->g.display, gi, gw->g.x, gw->g.y+z, gw->g.width, v, 0, gi->height-v); - } - } - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge - gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb - - // Horizontal slider - } else { - if (gsw->dpos != gw->g.width-1) // The unfilled area - gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.width-gsw->dpos, gw->g.height, gw->pstyle->enabled.progress); // Inactive area - if (gsw->dpos != 0) { // The filled area - for(z=0, v=gi->width; z < gsw->dpos; z += v) { - if (z+v > gsw->dpos) - v -= z+v - gsw->dpos; - gdispGImageDraw(gw->g.display, gi, gw->g.x+z, gw->g.y, v, gw->g.height, 0, 0); - } - } - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge - gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb - } - gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); - - #undef gsw -} -#endif /* GDISP_NEED_IMAGE */ - -#endif /* GFX_USE_GWIN && GWIN_NEED_BUTTON */ diff --git a/src/gwin/slider.h b/src/gwin/slider.h deleted file mode 100644 index 6cd8088b..00000000 --- a/src/gwin/slider.h +++ /dev/null @@ -1,157 +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/slider.h - * @brief GWIN Graphic window subsystem header file. - * - * @defgroup Slider Slider - * @ingroup Widgets - * - * @details Create sliders with different styles - * - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @pre GWIN_NEED_SLIDER must be set to TRUE in your gfxconf.h - * @{ - */ - -#ifndef _GWIN_SLIDER_H -#define _GWIN_SLIDER_H - -/* This file is included within "gwin/gwidget.h" */ - -#define GEVENT_GWIN_SLIDER (GEVENT_GWIN_CTRL_FIRST+1) - -typedef struct GEventGWinSlider { - GEventType type; // The type of this event (GEVENT_GWIN_BUTTON) - GHandle gwin; // The slider that is returning results - #if GWIN_WIDGET_TAGS - WidgetTag tag; // The slider tag - #endif - int position; -} GEventGWinSlider; - -// There are currently no GEventGWinSlider listening flags - use 0 - -// A slider window -typedef struct GSliderObject { - GWidgetObject w; - #if GINPUT_NEED_TOGGLE - uint16_t t_dn; - uint16_t t_up; - #endif - #if GINPUT_NEED_DIAL - uint16_t dial; - #endif - coord_t dpos; - int min; - int max; - int pos; -} GSliderObject; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create a slider 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] gb The GSliderObject structure to initialise. If this is NULL the structure is dynamically allocated. - * @param[in] pInit The initialization parameters to use - * - * @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 slider remembers its normal drawing state. If there is a window manager then it is automatically - * redrawn if the window is moved or its visibility state is changed. - * @note The initial slider range is from 0 to 100 with an initial position of 0. - * @note A slider supports mouse, toggle and dial input. - * @note When assigning a toggle, only one toggle is supported per role. If you try to assign more than - * one toggle to a role it will forget the previous toggle. Two roles are supported: - * Role 0 = toggle for down, Role 1 = toggle for up. - * @note When assigning a dial, only one dial is supported. If you try to assign more than one dial - * it will forget the previous dial. Only dial role 0 is supported. - * - * @api - */ -GHandle gwinGSliderCreate(GDisplay *g, GSliderObject *gb, const GWidgetInit *pInit); -#define gwinSliderCreate(w, pInit) gwinGSliderCreate(GDISP, w, pInit) - -/** - * @brief Set the slider range. - * - * @param[in] gh The window handle (must be a slider window) - * @param[in] min The minimum value - * @param[in] max The maximum value - * @note Sets the position to the minimum value. - * @note The slider is not automatically drawn. Call gwinSliderDraw() after changing the range. - * - * @api - */ -void gwinSliderSetRange(GHandle gh, int min, int max); - -/** - * @brief Set the slider position. - * - * @param[in] gh The window handle (must be a slider window) - * @param[in] pos The new position - * @note If the new position is outside the slider range then the position - * is set to the closest end of the range. - * @note The slider is not automatically drawn. Call gwinSliderDraw() after changing the position. - * - * @api - */ -void gwinSliderSetPosition(GHandle gh, int pos); - -/** - * @brief Get the current slider position. - * @return The slider position - * - * @param[in] gh The window handle (must be a slider window) - * - * @note The use of a listener to get the slider position is recommended if you - * want continuous updates on the slider position. - * - * @api - */ -#define gwinSliderGetPosition(gh) (((GSliderObject *)(gh))->pos) - -/** - * @brief Some custom slider drawing routines - * @details These function may be passed to @p gwinSetCustomDraw() to get different slider drawing styles - * - * @param[in] gw The widget (which must be a slider) - * @param[in] param A parameter passed in from the user - * - * @note In your custom slider drawing function you may optionally call this - * standard functions and then draw your extra details on top. - * @note The standard functions below ignore the param parameter except for @p gwinSliderDraw_Image(). - * @note The image custom draw function @p gwinSliderDraw_Image() uses param to pass in the gdispImage pointer. - * The image must be already opened before calling @p gwinSetCustomDraw(). The image is tiled to fill - * the active area of the slider. The normal colors apply to the border and inactive area and the dividing line - * between the active and inactive areas. - * No checking is done to compare the dimensions of the slider to the size of the image. - * Note text is drawn on top of the image. - * @note These custom drawing routines don't have to worry about setting clipping as the framework - * sets clipping to the object window prior to calling these routines. - * - * @api - * @{ - */ -void gwinSliderDraw_Std(GWidgetObject *gw, void *param); -void gwinSliderDraw_Image(GWidgetObject *gw, void *param); -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif /* _GWIN_SLIDER_H */ -/** @} */ diff --git a/src/gwin/sys_defs.h b/src/gwin/sys_defs.h index 7823eafe..5e08f7a2 100644 --- a/src/gwin/sys_defs.h +++ b/src/gwin/sys_defs.h @@ -950,26 +950,26 @@ extern "C" { /* Include widgets */ #if GWIN_NEED_WIDGET || defined(__DOXYGEN__) - #include "src/gwin/gwidget.h" + #include "gwin_widget.h" #endif /* Include containers */ #if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) - #include "src/gwin/gcontainer.h" + #include "gwin_container.h" #endif /* Include vanilla window objects */ #if GWIN_NEED_CONSOLE || defined(__DOXYGEN__) - #include "src/gwin/console.h" + #include "gwin_console.h" #endif #if GWIN_NEED_GRAPH || defined(__DOXYGEN__) - #include "src/gwin/graph.h" + #include "gwin_graph.h" #endif #if GWIN_NEED_IMAGE || defined(__DOXYGEN__) - #include "src/gwin/gimage.h" + #include "gwin_image.h" #endif #if GWIN_NEED_GL3D || defined(__DOXYGEN__) - #include "src/gwin/gwin_gl3d.h" + #include "gwin_gl3d.h" #endif #endif /* GFX_USE_GWIN */ diff --git a/src/gwin/sys_make.mk b/src/gwin/sys_make.mk index f905f8d3..81277a4e 100644 --- a/src/gwin/sys_make.mk +++ b/src/gwin/sys_make.mk @@ -1,18 +1,18 @@ -GFXSRC += $(GFXLIB)/src/gwin/gwin.c \ - $(GFXLIB)/src/gwin/gwidget.c \ - $(GFXLIB)/src/gwin/gwm.c \ - $(GFXLIB)/src/gwin/console.c \ - $(GFXLIB)/src/gwin/graph.c \ - $(GFXLIB)/src/gwin/button.c \ - $(GFXLIB)/src/gwin/slider.c \ - $(GFXLIB)/src/gwin/checkbox.c \ - $(GFXLIB)/src/gwin/gimage.c \ - $(GFXLIB)/src/gwin/label.c \ - $(GFXLIB)/src/gwin/radio.c \ - $(GFXLIB)/src/gwin/list.c \ - $(GFXLIB)/src/gwin/progressbar.c \ - $(GFXLIB)/src/gwin/gcontainer.c \ - $(GFXLIB)/src/gwin/frame.c \ +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_gl3d.c \ GFXINC += $(GFXLIB)/3rdparty/tinygl-0.4-ugfx/include -- cgit v1.2.3